1 #include "platform/wxwidgets/platform.hpp"
2 #include "platform/wxwidgets/textrender.hpp"
3 #include "platform/wxwidgets/scrollbar.hpp"
4 #include "platform/wxwidgets/loadsave.hpp"
5 #include "core/command.hpp"
6 #include "core/debug.hpp"
7 #include "core/instance.hpp"
8 #include "core/mainloop.hpp"
9 #include "core/memorymanip.hpp"
10 #include "core/project.hpp"
11 #include "interface/disassembler.hpp"
12 #include "library/minmax.hpp"
13 #include "library/hex.hpp"
14 #include "library/serialization.hpp"
16 #include <wx/clipbrd.h>
17 #include <wx/msgdlg.h>
19 #include <wx/button.h>
20 #include <wx/checkbox.h>
21 #include <wx/listbox.h>
22 #include <wx/stattext.h>
23 #include <wx/combobox.h>
24 #include <wx/textctrl.h>
25 #include <wx/spinctrl.h>
26 #include <wx/statusbr.h>
27 #include <wx/dataobj.h>
29 #include <boost/regex.hpp>
36 wxID_FIND_NEXT
= wxID_HIGHEST
+ 1,
48 int prompt_for_save(wxWindow
* parent
, const std::string
& what
)
50 wxMessageDialog
* d
= new wxMessageDialog(parent
, towxstring(what
+ " has unsaved changes, "
51 "save before closing?"), towxstring("Save on exit?"), wxCENTER
| wxYES_NO
| wxCANCEL
|
53 d
->SetYesNoCancelLabels(wxT("Save"), wxT("Discard"), wxT("Cancel"));
54 int r
= d
->ShowModal();
56 if(r
== wxID_YES
) return 1;
57 if(r
== wxID_NO
) return 0;
58 if(r
== wxID_CANCEL
) return -1;
62 class dialog_find
: public wxDialog
65 dialog_find(wxWindow
* parent
);
66 std::string
get_pattern();
67 void on_ok(wxCommandEvent
& e
) { EndModal(wxID_OK
); }
68 void on_cancel(wxCommandEvent
& e
) { EndModal(wxID_CANCEL
); }
76 dialog_find::dialog_find(wxWindow
* parent
)
77 : wxDialog(parent
, wxID_ANY
, wxT("Find"))
79 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
81 wxBoxSizer
* t_s
= new wxBoxSizer(wxHORIZONTAL
);
82 t_s
->Add(type
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
83 0, NULL
, wxCB_READONLY
), 1, wxGROW
);
84 type
->Append(towxstring("Literal"));
85 type
->Append(towxstring("Wildcards"));
86 type
->Append(towxstring("Regexp"));
87 type
->SetSelection(0);
88 t_s
->Add(text
= new wxTextCtrl(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxSize(350, -1),
89 wxTE_PROCESS_ENTER
), 0, wxGROW
);
90 top_s
->Add(t_s
, 1, wxGROW
);
91 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
92 pbutton_s
->AddStretchSpacer();
93 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("OK")));
94 pbutton_s
->Add(cancel
= new wxButton(this, wxID_ANY
, wxT("Cancel")));
95 text
->Connect(wxEVT_COMMAND_TEXT_ENTER
, wxCommandEventHandler(dialog_find::on_ok
), NULL
, this);
96 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_find::on_ok
), NULL
, this);
97 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_find::on_cancel
), NULL
,
99 top_s
->Add(pbutton_s
, 0, wxGROW
);
100 top_s
->SetSizeHints(this);
104 std::string
dialog_find::get_pattern()
106 if(tostdstring(text
->GetValue()) == "")
108 if(type
->GetSelection() == 2)
109 return "R" + tostdstring(text
->GetValue());
110 else if(type
->GetSelection() == 1)
111 return "W" + tostdstring(text
->GetValue());
113 return "F" + tostdstring(text
->GetValue());
116 class dialog_disassemble
: public wxDialog
119 dialog_disassemble(wxWindow
* parent
);
120 dialog_disassemble(wxWindow
* parent
, uint64_t dflt_base
, const std::string
& dflt_lang
);
121 std::string
get_disassembler();
122 uint64_t get_address();
123 uint64_t get_count();
124 void on_change(wxCommandEvent
& e
);
125 void on_ok(wxCommandEvent
& e
);
126 void on_cancel(wxCommandEvent
& e
) { EndModal(wxID_CANCEL
); }
128 void init(bool spec
, uint64_t dflt_base
, std::string dflt_lang
);
138 static std::string old_dflt_lang
;
139 static uint64_t old_dflt_base
;
142 std::string
dialog_disassemble::old_dflt_lang
;
143 uint64_t dialog_disassemble::old_dflt_base
;
145 dialog_disassemble::dialog_disassemble(wxWindow
* parent
)
146 : wxDialog(parent
, wxID_ANY
, wxT("Disassemble region"))
151 dialog_disassemble::dialog_disassemble(wxWindow
* parent
, uint64_t dflt_base
, const std::string
& dflt_lang
)
152 : wxDialog(parent
, wxID_ANY
, wxT("Disassemble region"))
154 init(true, dflt_base
, dflt_lang
);
157 void dialog_disassemble::init(bool spec
, uint64_t dflt_base
, std::string dflt_lang
)
159 std::map
<std::string
, std::pair
<uint64_t, uint64_t>> regions
;
160 std::set
<std::string
> disasms
;
161 lsnes_instance
.iqueue
.run([®ions
, &disasms
]() {
162 for(auto i
: lsnes_instance
.memory
.get_regions())
163 regions
[i
->name
] = std::make_pair(i
->base
, i
->size
);
164 disasms
= disassembler::list();
167 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
170 wxBoxSizer
* type_s
= new wxBoxSizer(wxHORIZONTAL
);
171 type_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Language:")), 0, wxGROW
);
172 type_s
->Add(type
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
173 0, NULL
, wxCB_READONLY
), 0, wxGROW
);
174 for(auto& i
: disasms
)
175 type
->Append(towxstring(i
));
176 code_types
= type
->GetCount();
177 type
->Append(towxstring("Data (signed byte)"));
178 type
->Append(towxstring("Data (unsigned byte)"));
179 type
->Append(towxstring("Data (hex byte)"));
180 type
->Append(towxstring("Data (signed word)"));
181 type
->Append(towxstring("Data (unsigned word)"));
182 type
->Append(towxstring("Data (hex word)"));
183 type
->Append(towxstring("Data (signed onehalfword)"));
184 type
->Append(towxstring("Data (unsigned onehalfword)"));
185 type
->Append(towxstring("Data (hex onehalfword)"));
186 type
->Append(towxstring("Data (signed doubleword)"));
187 type
->Append(towxstring("Data (unsigned doubleword)"));
188 type
->Append(towxstring("Data (hex doubleword)"));
189 type
->Append(towxstring("Data (signed quadword)"));
190 type
->Append(towxstring("Data (unsigned quadword)"));
191 type
->Append(towxstring("Data (hex quadword)"));
192 type
->Append(towxstring("Data (float)"));
193 type
->Append(towxstring("Data (double)"));
194 type
->SetSelection(0);
195 type
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, wxCommandEventHandler(dialog_disassemble::on_change
),
197 top_s
->Add(type_s
, 0, wxGROW
);
199 wxBoxSizer
* endian_s
= new wxBoxSizer(wxHORIZONTAL
);
200 endian_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Endian:")), 0, wxGROW
);
201 endian_s
->Add(endian
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
202 0, NULL
, wxCB_READONLY
), 0, wxGROW
);
203 endian
->Append(towxstring("(Memory area default)"));
204 endian
->Append(towxstring("Little-endian"));
205 endian
->Append(towxstring("Host-endian"));
206 endian
->Append(towxstring("Big-endian"));
207 endian
->SetSelection(0);
208 endian
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, wxCommandEventHandler(dialog_disassemble::on_change
),
210 top_s
->Add(endian_s
, 0, wxGROW
);
212 wxBoxSizer
* vma_s
= new wxBoxSizer(wxHORIZONTAL
);
213 vma_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Area:")), 0, wxGROW
);
214 vma_s
->Add(vma
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
215 0, NULL
, wxCB_READONLY
), 0, wxGROW
);
216 vma
->Append(towxstring("(Any)"));
217 for(auto& i
: regions
)
218 vma
->Append(towxstring(i
.first
));
219 vma
->SetSelection(0);
220 vma
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, wxCommandEventHandler(dialog_disassemble::on_change
),
222 top_s
->Add(vma_s
, 0, wxGROW
);
224 wxBoxSizer
* addr_s
= new wxBoxSizer(wxHORIZONTAL
);
225 addr_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Address:")), 0, wxGROW
);
226 addr_s
->Add(address
= new wxTextCtrl(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxSize(200, -1)),
228 address
->Connect(wxEVT_COMMAND_TEXT_UPDATED
, wxCommandEventHandler(dialog_disassemble::on_change
),
230 top_s
->Add(addr_s
, 0, wxGROW
);
232 wxBoxSizer
* cnt_s
= new wxBoxSizer(wxHORIZONTAL
);
233 cnt_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Count:")), 0, wxGROW
);
234 cnt_s
->Add(count
= new wxSpinCtrl(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
235 wxSP_ARROW_KEYS
, 1, 1000000000, 10), 0, wxGROW
);
236 count
->Connect(wxEVT_SPINCTRL
, wxCommandEventHandler(dialog_disassemble::on_change
), NULL
,
238 top_s
->Add(cnt_s
, 0, wxGROW
);
240 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
241 pbutton_s
->AddStretchSpacer();
242 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("OK")));
243 pbutton_s
->Add(cancel
= new wxButton(this, wxID_ANY
, wxT("Cancel")));
244 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_disassemble::on_ok
), NULL
,
246 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_disassemble::on_cancel
),
248 top_s
->Add(pbutton_s
, 0, wxGROW
);
249 top_s
->SetSizeHints(this);
254 dflt_lang
= old_dflt_lang
;
255 dflt_base
= old_dflt_base
;
257 //Set default language.
258 if(regex_match("\\$data:.*", dflt_lang
)) {
259 switch(dflt_lang
[6]) {
260 case 'b': type
->SetSelection(code_types
+ 0); break;
261 case 'B': type
->SetSelection(code_types
+ 1); break;
262 case 'c': type
->SetSelection(code_types
+ 2); break;
263 case 'w': type
->SetSelection(code_types
+ 3); break;
264 case 'W': type
->SetSelection(code_types
+ 4); break;
265 case 'C': type
->SetSelection(code_types
+ 5); break;
266 case 'h': type
->SetSelection(code_types
+ 6); break;
267 case 'H': type
->SetSelection(code_types
+ 7); break;
268 case 'i': type
->SetSelection(code_types
+ 8); break;
269 case 'd': type
->SetSelection(code_types
+ 9); break;
270 case 'D': type
->SetSelection(code_types
+ 10); break;
271 case 'I': type
->SetSelection(code_types
+ 11); break;
272 case 'q': type
->SetSelection(code_types
+ 12); break;
273 case 'Q': type
->SetSelection(code_types
+ 13); break;
274 case 'r': type
->SetSelection(code_types
+ 14); break;
275 case 'f': type
->SetSelection(code_types
+ 15); break;
276 case 'F': type
->SetSelection(code_types
+ 16); break;
278 switch(dflt_lang
[7]) {
279 case 'l': endian
->SetSelection(1); break;
280 case 'h': endian
->SetSelection(2); break;
281 case 'b': endian
->SetSelection(3); break;
285 //Set default disasm.
286 for(auto& i
: disasms
) {
291 if(j
< disasms
.size())
292 type
->SetSelection(j
);
294 //Set default address.
296 for(auto& i
: regions
) {
297 if(dflt_base
>= i
.second
.first
&& dflt_base
< i
.second
.first
+ i
.second
.second
) {
298 vma
->SetSelection(k
+ 1);
299 dflt_base
-= i
.second
.first
;
304 address
->SetValue(towxstring((stringfmt() << std::hex
<< dflt_base
).str()));
310 void dialog_disassemble::on_ok(wxCommandEvent
& e
)
315 std::string
dialog_disassemble::get_disassembler()
317 if(type
->GetSelection() >= (ssize_t
)code_types
&& type
->GetSelection() < (ssize_t
)type
->GetCount()) {
318 int _endian
= endian
->GetSelection();
319 int dtsel
= type
->GetSelection() - code_types
;
320 std::string _vma
= tostdstring(vma
->GetStringSelection());
321 if(_endian
<= 0 || _endian
> 3) {
323 lsnes_instance
.iqueue
.run([&_endian
, _vma
]() {
324 for(auto i
: lsnes_instance
.memory
.get_regions()) {
325 if(i
->name
== _vma
) {
326 _endian
= i
->endian
+ 2;
331 if(dtsel
< 0) dtsel
= 0;
332 if(dtsel
> 16) dtsel
= 16;
333 static const char* typechars
= "bBcwWChHidDIqQrfF";
334 static const char* endianchars
= " lhb";
335 std::string res
= std::string("$data:") + std::string(1, typechars
[dtsel
]) +
336 std::string(1, endianchars
[_endian
]);
341 std::string res
= tostdstring(type
->GetStringSelection());
348 uint64_t dialog_disassemble::get_address()
351 if(vma
->GetSelection() && vma
->GetSelection() != wxNOT_FOUND
) {
352 std::string _vma
= tostdstring(vma
->GetStringSelection());
353 lsnes_instance
.iqueue
.run([&base
, _vma
]() {
354 for(auto i
: lsnes_instance
.memory
.get_regions()) {
355 if(i
->name
== _vma
) {
361 uint64_t off
= hex::from
<uint64_t>(tostdstring(address
->GetValue()));
362 uint64_t res
= base
+ off
;
368 uint64_t dialog_disassemble::get_count()
370 return count
->GetValue();
373 void dialog_disassemble::on_change(wxCommandEvent
& e
)
377 hex::from
<uint64_t>(tostdstring(address
->GetValue()));
378 } catch(std::exception
& e
) {
381 is_ok
= is_ok
&& (type
->GetSelection() != wxNOT_FOUND
);
382 is_ok
= is_ok
&& (vma
->GetSelection() != wxNOT_FOUND
);
383 endian
->Enable(type
->GetSelection() >= (ssize_t
)code_types
&& type
->GetSelection() <
384 (ssize_t
)type
->GetCount());
385 is_ok
= is_ok
&& (!endian
->IsEnabled() || endian
->GetSelection() != wxNOT_FOUND
);
386 //If VMA is global, ensure there is valid endian.
387 is_ok
= is_ok
&& (vma
->GetSelection() != 0 || !endian
->IsEnabled() || endian
->GetSelection() != 0);
391 class wxwin_tracelog
;
393 class dialog_breakpoint_add
: public wxDialog
396 dialog_breakpoint_add(wxWindow
* parent
, std::list
<memory_region
*> regions
);
397 std::pair
<uint64_t, debug_context::etype
> get_result();
398 void on_ok(wxCommandEvent
& e
) { EndModal(wxID_OK
); }
399 void on_cancel(wxCommandEvent
& e
) { EndModal(wxID_CANCEL
); }
400 void on_address_change(wxCommandEvent
& e
);
402 std::list
<memory_region
*> regions
;
410 dialog_breakpoint_add::dialog_breakpoint_add(wxWindow
* parent
, std::list
<memory_region
*> _regions
)
411 : wxDialog(parent
, wxID_ANY
, wxT("Add breakpoint"))
414 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
417 top_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Memory region:")), 0, wxGROW
);
418 top_s
->Add(vmasel
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
419 0, NULL
, wxCB_READONLY
), 1, wxGROW
);
420 vmasel
->Append(towxstring(""));
421 for(auto i
: regions
)
422 vmasel
->Append(towxstring(i
->name
));
423 vmasel
->SetSelection(0);
425 top_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Offset (hexadecimal):")), 0, wxGROW
);
426 top_s
->Add(address
= new wxTextCtrl(this, wxID_ANY
, wxT("0"), wxDefaultPosition
, wxSize(350, -1)), 0,
429 top_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Breakpoint type:")), 0, wxGROW
);
430 top_s
->Add(typesel
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
431 0, NULL
, wxCB_READONLY
), 1, wxGROW
);
432 typesel
->Append(towxstring("Read"));
433 typesel
->Append(towxstring("Write"));
434 typesel
->Append(towxstring("Execute"));
435 typesel
->SetSelection(0);
437 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
438 pbutton_s
->AddStretchSpacer();
439 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("OK")));
440 pbutton_s
->Add(cancel
= new wxButton(this, wxID_ANY
, wxT("Cancel")));
441 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoint_add::on_ok
), NULL
,
443 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoint_add::on_cancel
),
445 top_s
->Add(pbutton_s
, 0, wxGROW
);
446 top_s
->SetSizeHints(this);
450 void dialog_breakpoint_add::on_address_change(wxCommandEvent
& e
)
453 hex::from
<uint64_t>(tostdstring(address
->GetValue()));
460 std::pair
<uint64_t, debug_context::etype
> dialog_breakpoint_add::get_result()
462 std::string vmaname
= tostdstring(vmasel
->GetStringSelection());
463 std::string addrtext
= tostdstring(address
->GetValue());
466 for(auto i
: regions
)
467 if(i
->name
== vmaname
)
472 addr
= base
+ hex::from
<uint64_t>(addrtext
);
473 } catch(std::exception
& e
) {
476 debug_context::etype dtype
= debug_context::DEBUG_EXEC
;
477 if(typesel
->GetSelection() == 0)
478 dtype
= debug_context::DEBUG_READ
;
479 if(typesel
->GetSelection() == 1)
480 dtype
= debug_context::DEBUG_WRITE
;
481 if(typesel
->GetSelection() == 2)
482 dtype
= debug_context::DEBUG_EXEC
;
483 return std::make_pair(addr
, dtype
);
487 class dialog_breakpoints
: public wxDialog
490 dialog_breakpoints(wxwin_tracelog
* parent
);
491 void on_ok(wxCommandEvent
& e
) { EndModal(wxID_OK
); }
492 void on_add(wxCommandEvent
& e
);
493 void on_delete(wxCommandEvent
& e
);
494 void on_selchange(wxCommandEvent
& e
);
496 std::string
format_line(std::pair
<uint64_t, debug_context::etype
> entry
);
497 size_t get_insert_pos(std::pair
<uint64_t, debug_context::etype
> entry
);
498 void populate_breakpoints();
499 std::list
<memory_region
*> regions
;
504 wxwin_tracelog
* pwin
;
505 std::vector
<std::pair
<uint64_t, debug_context::etype
>> listsyms
;
508 class wxwin_tracelog
: public wxFrame
, public debug_context::callback_base
511 wxwin_tracelog(wxWindow
* parent
, int _cpuid
, const std::string
& cpuname
);
513 bool ShouldPreventAppExit() const { return false; }
514 scroll_bar
* get_scroll() { return scroll
; }
515 void on_wclose(wxCloseEvent
& e
);
516 void on_enabled(wxCommandEvent
& e
);
517 void on_menu(wxCommandEvent
& e
);
518 void process_lines();
519 uint64_t get_find_line() { return find_active
? find_line
: 0xFFFFFFFFFFFFFFFFULL
; }
520 std::set
<std::pair
<uint64_t, debug_context::etype
>> get_breakpoints();
521 void add_breakpoint(uint64_t addr
, debug_context::etype dtype
);
522 void remove_breakpoint(uint64_t addr
, debug_context::etype dtype
);
524 class _panel
: public text_framebuffer_panel
527 _panel(wxwin_tracelog
* parent
);
528 void on_size(wxSizeEvent
& e
);
529 void on_mouse(wxMouseEvent
& e
);
530 wxSize
DoGetBestSize() const;
532 std::vector
<std::string
> rows
;
533 void on_popup_menu(wxCommandEvent
& e
);
534 bool scroll_to_end_on_repaint
;
536 void prepare_paint();
538 uint64_t pressed_row
;
539 uint64_t current_row
;
544 void scroll_pane(uint64_t line
);
546 volatile bool trace_active
;
547 void callback(const debug_context::params
& params
);
548 void killed(uint64_t addr
, debug_context::etype type
);
549 void do_rwx_break(uint64_t addr
, uint64_t value
, debug_context::etype type
);
550 void kill_debug_hooks();
556 threads::lock buffer_mutex
;
557 std::list
<std::string
> lines_waiting
;
558 bool unprocessed_lines
;
562 std::string find_string
;
565 std::map
<std::pair
<uint64_t, debug_context::etype
>, bool> rwx_breakpoints
;
566 wxMenuItem
* m_singlestep
;
569 wxwin_tracelog::~wxwin_tracelog()
573 void wxwin_tracelog::on_wclose(wxCloseEvent
& e
)
575 if(dirty
&& !wxwidgets_exiting
) {
576 int r
= prompt_for_save(this, "Trace log");
577 if(r
< 0 || (r
> 0 && !do_exit_save()))
581 lsnes_instance
.iqueue
.run([this]() { kill_debug_hooks(); });
582 trace_active
= false;
588 void wxwin_tracelog::kill_debug_hooks()
590 CORE().dbg
.remove_callback(cpuid
, debug_context::DEBUG_TRACE
, *this);
591 CORE().dbg
.remove_callback(cpuid
, debug_context::DEBUG_FRAME
, *this);
592 threads::alock
h(buffer_mutex
);
593 for(auto& i
: rwx_breakpoints
) {
596 CORE().dbg
.remove_callback(i
.first
.first
, i
.first
.second
, *this);
599 trace_active
= false;
600 convert_break_to_pause();
603 wxwin_tracelog::_panel::_panel(wxwin_tracelog
* parent
)
604 : text_framebuffer_panel(parent
, 20, 5, wxID_ANY
, NULL
)
611 scroll_to_end_on_repaint
= false;
612 this->Connect(wxEVT_SIZE
, wxSizeEventHandler(wxwin_tracelog::_panel::on_size
), NULL
, this);
613 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
614 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
615 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
616 this->Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
619 void wxwin_tracelog::_panel::on_size(wxSizeEvent
& e
)
621 wxSize newsize
= e
.GetSize();
622 auto tcell
= get_cell();
623 size_t lines
= newsize
.y
/ tcell
.second
;
624 size_t linelen
= newsize
.x
/ tcell
.first
;
625 if(lines
< 1) lines
= 1;
626 if(linelen
< 1) linelen
= 1;
627 set_size(linelen
, lines
);
628 p
->get_scroll()->set_page_size(lines
);
633 void wxwin_tracelog::_panel::on_mouse(wxMouseEvent
& e
)
635 uint64_t local_line
= pos
+ e
.GetY() / get_cell().second
;
637 if(local_line
< rows
.size()) {
639 pressed_row
= local_line
;
641 }else if(e
.RightUp()) {
644 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
,
645 wxCommandEventHandler(wxwin_tracelog::_panel::on_popup_menu
), NULL
, this);
646 menu
.Append(wxID_COPY
, wxT("Copy to clipboard"));
647 menu
.Append(wxID_SAVE
, wxT("Save to file"));
648 menu
.AppendSeparator();
649 menu
.Append(wxID_DELETE
, wxT("Delete"));
652 current_row
= min(local_line
, static_cast<uint64_t>(rows
.size()));
658 p
->get_scroll()->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
661 wxSize
wxwin_tracelog::_panel::DoGetBestSize() const
663 return wxSize(120 * 8, 25 * 16);
666 void wxwin_tracelog::_panel::prepare_paint()
668 p
->get_scroll()->set_range(rows
.size());
669 if(scroll_to_end_on_repaint
) {
670 scroll_to_end_on_repaint
= false;
671 p
->get_scroll()->set_position(rows
.size());
672 pos
= p
->get_scroll()->get_position();
674 uint64_t m
= min(pressed_row
, current_row
);
675 uint64_t M
= max(pressed_row
, current_row
);
676 auto s
= get_characters();
677 uint64_t fline
= p
->get_find_line();
678 for(uint64_t i
= pos
; i
< pos
+ s
.second
&& i
< rows
.size(); i
++) {
679 bool selected
= holding
&& (i
>= m
) && (i
<= M
);
680 bool isfl
= (i
== fline
);
681 uint32_t fg
= selected
? 0x0000FF : 0x000000;
682 uint32_t bg
= selected
? 0x000000 : (isfl
? 0xC0FFC0 : 0xFFFFFF);
683 write(rows
[i
], s
.first
, 0, i
- pos
, fg
, bg
);
685 for(uint64_t i
= rows
.size(); i
< pos
+ s
.second
; i
++)
686 write("", s
.first
, 0, i
- pos
, 0xFFFFFF, 0xFFFFFF);
689 void wxwin_tracelog::process_lines()
691 threads::alock
h(this->buffer_mutex
);
692 size_t osize
= panel
->rows
.size();
694 panel
->rows
.push_back(std::string(120, '-'));
697 for(auto& i
: lines_waiting
)
698 panel
->rows
.push_back(i
);
699 lines_waiting
.clear();
700 unprocessed_lines
= false;
701 if(panel
->rows
.size() != osize
) {
702 panel
->scroll_to_end_on_repaint
= true;
705 panel
->request_paint();
708 void wxwin_tracelog::do_rwx_break(uint64_t addr
, uint64_t value
, debug_context::etype type
)
710 lsnes_instance
.dbg
.request_break();
713 void wxwin_tracelog::callback(const debug_context::params
& p
)
716 case debug_context::DEBUG_READ
:
717 case debug_context::DEBUG_WRITE
:
718 case debug_context::DEBUG_EXEC
:
719 do_rwx_break(p
.rwx
.addr
, p
.rwx
.value
, p
.type
);
721 case debug_context::DEBUG_TRACE
: {
724 //Got tracelog line, send it.
725 threads::alock
h(buffer_mutex
);
726 lines_waiting
.push_back(p
.trace
.decoded_insn
);
727 if(!unprocessed_lines
) {
728 unprocessed_lines
= true;
729 runuifun([this]() { this->process_lines(); });
731 if(singlestepping
&& p
.trace
.true_insn
) {
732 lsnes_instance
.dbg
.request_break();
733 singlestepping
= false;
737 case debug_context::DEBUG_FRAME
: {
738 std::ostringstream xstr
;
739 xstr
<< "------------ ";
740 xstr
<< "Frame " << p
.frame
.frame
;
741 if(p
.frame
.loadstated
) xstr
<< " (loadstated)";
742 xstr
<< " ------------";
743 std::string str
= xstr
.str();
744 threads::alock
h(buffer_mutex
);
745 lines_waiting
.push_back(str
);
746 if(!unprocessed_lines
) {
747 unprocessed_lines
= true;
748 runuifun([this]() { this->process_lines(); });
755 void wxwin_tracelog::killed(uint64_t addr
, debug_context::etype type
)
758 case debug_context::DEBUG_READ
:
759 case debug_context::DEBUG_WRITE
:
760 case debug_context::DEBUG_EXEC
: {
761 //We need to kill this hook if still active.
762 auto i2
= std::make_pair(addr
, type
);
763 auto& h
= rwx_breakpoints
[i2
];
765 lsnes_instance
.dbg
.remove_callback(addr
, type
, *this);
769 case debug_context::DEBUG_TRACE
:
775 this->enabled
->SetValue(false);
776 this->enabled
->Enable(false);
777 this->m_singlestep
->Enable(false);
780 case debug_context::DEBUG_FRAME
:
786 void wxwin_tracelog::on_enabled(wxCommandEvent
& e
)
788 bool enable
= enabled
->GetValue();
789 lsnes_instance
.iqueue
.run([this, enable
]() {
791 threads::alock
h(buffer_mutex
);
794 for(auto& i
: rwx_breakpoints
) {
796 lsnes_instance
.dbg
.add_callback(i2
.first
, i2
.second
, *this);
799 lsnes_instance
.dbg
.add_callback(cpuid
, debug_context::DEBUG_TRACE
, *this);
800 lsnes_instance
.dbg
.add_callback(0, debug_context::DEBUG_FRAME
, *this);
801 this->trace_active
= true;
802 } else if(trace_active
) {
803 this->trace_active
= false;
804 this->kill_debug_hooks();
807 m_singlestep
->Enable(enable
);
810 bool find_match(const std::string
& pattern
, const std::string
& candidate
)
812 static std::string last_find
;
813 static boost::regex regex
;
816 if(pattern
[0] == 'F') {
818 if(pattern
!= last_find
) {
819 std::string tmp
= pattern
;
821 regex
= boost::regex(tmp
, boost::regex_constants::literal
|
822 boost::regex_constants::icase
);
826 if(pattern
[0] == 'W') {
828 if(pattern
!= last_find
) {
829 std::ostringstream y
;
830 for(size_t i
= 1; i
< pattern
.length(); i
++)
831 if(pattern
[i
] == '?')
833 else if(pattern
[i
] == '*')
835 else if(pattern
[i
] >= 'A' && pattern
[i
] <= 'Z')
837 else if(pattern
[i
] >= 'a' && pattern
[i
] <= 'z')
839 else if(pattern
[i
] >= '0' && pattern
[i
] <= '9')
842 y
<< "\\" << pattern
[i
];
843 std::string tmp
= y
.str();
844 regex
= boost::regex(tmp
, boost::regex_constants::extended
);
848 if(pattern
[0] == 'R') {
850 if(pattern
!= last_find
) {
851 std::string tmp
= pattern
;
853 regex
= boost::regex(tmp
, boost::regex_constants::extended
|
854 boost::regex_constants::icase
);
858 return regex_search(candidate
, regex
);
861 void wxwin_tracelog::on_menu(wxCommandEvent
& e
)
863 if(e
.GetId() == wxID_EXIT
) {
865 int r
= prompt_for_save(this, "Trace log");
866 if(r
< 0 || (r
> 0 && !do_exit_save()))
870 lsnes_instance
.iqueue
.run([this]() { this->kill_debug_hooks(); });
872 trace_active
= false;
875 } else if(e
.GetId() == wxID_SAVE
) {
877 std::string filename
= choose_file_save(this, "Save tracelog to",
878 lsnes_instance
.project
.otherpath(), filetype_trace
);
879 std::ofstream
s(filename
, std::ios::app
);
880 if(!s
) throw std::runtime_error("Error opening output file");
881 for(auto& i
: panel
->rows
)
883 if(!s
) throw std::runtime_error("Error writing output file");
885 } catch(canceled_exception
& e
) {
886 } catch(std::exception
& e
) {
887 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
890 } else if(e
.GetId() == wxID_FIND
) {
892 dialog_find
* d
= new dialog_find(this);
893 if(d
->ShowModal() != wxID_OK
) {
897 tmp
= d
->get_pattern();
906 while(find_line
< panel
->rows
.size()) {
907 if(find_match(find_string
, panel
->rows
[find_line
]))
911 if(find_line
== panel
->rows
.size()) {
914 wxMessageBox(towxstring("Found nothing appropriate"), _T("Not found"),
915 wxICON_EXCLAMATION
| wxOK
, this);
917 scroll_pane(find_line
);
918 } else if(e
.GetId() == wxID_FIND_NEXT
) {
921 uint64_t old_find_line
= find_line
;
923 while(!panel
->rows
.empty() && find_line
!= old_find_line
) {
924 if(find_line
>= panel
->rows
.size())
926 if(find_match(find_string
, panel
->rows
[find_line
]))
930 scroll_pane(find_line
);
931 } else if(e
.GetId() == wxID_FIND_PREV
) {
934 uint64_t old_find_line
= find_line
;
936 while(!panel
->rows
.empty() && find_line
!= old_find_line
) {
937 if(find_line
>= panel
->rows
.size())
938 find_line
= panel
->rows
.size() - 1;
939 if(find_match(find_string
, panel
->rows
[find_line
]))
943 scroll_pane(find_line
);
944 } else if(e
.GetId() == wxID_SINGLESTEP
) {
945 lsnes_instance
.iqueue
.run_async([this]() {
946 this->singlestepping
= true;
947 lsnes_instance
.command
.invoke("unpause-emulator");
948 }, [](std::exception
& e
) {});
949 } else if(e
.GetId() == wxID_FRAMEADVANCE
) {
950 lsnes_instance
.iqueue
.run_async([this]() {
951 lsnes_instance
.command
.invoke("+advance-frame");
952 lsnes_instance
.command
.invoke("-advance-frame");
953 }, [](std::exception
& e
) {});
954 } else if(e
.GetId() == wxID_CONTINUE
) {
955 lsnes_instance
.iqueue
.run_async([this]() {
956 lsnes_instance
.command
.invoke("unpause-emulator");
957 }, [](std::exception
& e
) {});
958 } else if(e
.GetId() == wxID_BREAKPOINTS
) {
959 dialog_breakpoints
* d
= new dialog_breakpoints(this);
962 } else if(e
.GetId() == wxID_CLEAR
) {
963 int r
= prompt_for_save(this, "Trace log");
964 if(r
< 0 || (r
> 0 && !do_exit_save()))
967 panel
->request_paint();
972 void wxwin_tracelog::_panel::on_popup_menu(wxCommandEvent
& e
)
975 uint64_t m
= min(pressed_row
, current_row
);
976 uint64_t M
= max(pressed_row
, current_row
) + 1;
977 m
= min(m
, (uint64_t)rows
.size());
978 M
= min(M
, (uint64_t)rows
.size());
981 for(uint64_t i
= m
; i
< M
&& i
< rows
.size(); i
++) {
983 std::string mline
= rows
[i
];
984 if(lines
== 1) str
+= "\n";
986 if(lines
>= 1) str
+= "\n";
994 if (wxTheClipboard
->Open()) {
995 wxTheClipboard
->SetData(new wxTextDataObject(towxstring(str
)));
996 wxTheClipboard
->Close();
1001 std::string filename
= choose_file_save(this, "Save tracelog fragment to",
1002 lsnes_instance
.project
.otherpath(), filetype_trace
);
1003 std::ofstream
s(filename
, std::ios::app
);
1004 if(!s
) throw std::runtime_error("Error opening output file");
1005 if(lines
== 1) str
+= "\n";
1007 if(!s
) throw std::runtime_error("Error writing output file");
1008 } catch(canceled_exception
& e
) {
1009 } catch(std::exception
& e
) {
1010 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1015 rows
.erase(rows
.begin() + m
, rows
.begin() + M
);
1023 void wxwin_tracelog::scroll_pane(uint64_t line
)
1025 unsigned r
= panel
->get_characters().second
;
1026 unsigned offset
= r
/ 2;
1028 scroll
->set_position(panel
->pos
= 0);
1029 else if(line
+ r
<= panel
->rows
.size())
1030 scroll
->set_position(panel
->pos
= line
- offset
);
1032 scroll
->set_position(panel
->pos
= panel
->rows
.size() - r
);
1033 panel
->request_paint();
1036 bool wxwin_tracelog::do_exit_save()
1040 std::string filename
= choose_file_save(this, "Save tracelog to",
1041 lsnes_instance
.project
.otherpath(), filetype_trace
);
1042 std::ofstream
s(filename
, std::ios::app
);
1043 if(!s
) throw std::runtime_error("Error opening output file");
1044 for(auto& i
: panel
->rows
)
1045 s
<< i
<< std::endl
;
1046 if(!s
) throw std::runtime_error("Error writing output file");
1048 } catch(canceled_exception
& e
) {
1050 } catch(std::exception
& e
) {
1051 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1058 std::set
<std::pair
<uint64_t, debug_context::etype
>> wxwin_tracelog::get_breakpoints()
1060 std::set
<std::pair
<uint64_t, debug_context::etype
>> ret
;
1061 lsnes_instance
.iqueue
.run([this, &ret
]() {
1062 for(auto i
: rwx_breakpoints
)
1063 ret
.insert(i
.first
);
1068 void wxwin_tracelog::add_breakpoint(uint64_t addr
, debug_context::etype dtype
)
1070 std::pair
<uint64_t, debug_context::etype
> i2
= std::make_pair(addr
, dtype
);
1072 //We'll register this later.
1073 rwx_breakpoints
[i2
] = false;
1076 lsnes_instance
.dbg
.add_callback(i2
.first
, i2
.second
, *this);
1077 rwx_breakpoints
[i2
] = true;
1080 void wxwin_tracelog::remove_breakpoint(uint64_t addr
, debug_context::etype dtype
)
1082 std::pair
<uint64_t, debug_context::etype
> i2
= std::make_pair(addr
, dtype
);
1083 auto& h
= rwx_breakpoints
[i2
];
1085 lsnes_instance
.dbg
.remove_callback(i2
.first
, i2
.second
, *this);
1086 rwx_breakpoints
.erase(i2
);
1089 wxwin_tracelog::wxwin_tracelog(wxWindow
* parent
, int _cpuid
, const std::string
& cpuname
)
1090 : wxFrame(parent
, wxID_ANY
, towxstring("lsnes: Tracelog for " + cpuname
), wxDefaultPosition
,
1091 wxDefaultSize
, wxMINIMIZE_BOX
| wxRESIZE_BORDER
| wxSYSTEM_MENU
| wxCAPTION
| wxCLOSE_BOX
|
1095 singlestepping
= false;
1096 find_active
= false;
1099 trace_active
= false;
1100 unprocessed_lines
= false;
1104 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
1106 wxBoxSizer
* bottom_s
= new wxBoxSizer(wxHORIZONTAL
);
1107 top_s
->Add(enabled
= new wxCheckBox(this, wxID_ANY
, wxT("Enabled")), 0, wxGROW
);
1108 bottom_s
->Add(panel
= new _panel(this), 1, wxGROW
);
1109 bottom_s
->Add(scroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
1110 top_s
->Add(bottom_s
, 1, wxGROW
);
1111 enabled
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
, wxCommandEventHandler(wxwin_tracelog::on_enabled
),
1113 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_tracelog::on_wclose
),
1115 scroll
->set_page_size(panel
->get_characters().second
);
1116 scroll
->set_handler([this](scroll_bar
& s
) {
1117 this->panel
->pos
= s
.get_position();
1118 this->panel
->request_paint();
1124 SetMenuBar(mb
= new wxMenuBar
);
1125 SetStatusBar(sb
= new wxStatusBar(this));
1126 mb
->Append(menu
= new wxMenu(), wxT("File"));
1127 menu
->Append(wxID_SAVE
, wxT("Save"));
1128 menu
->AppendSeparator();
1129 menu
->Append(wxID_EXIT
, wxT("Close"));
1130 mb
->Append(menu
= new wxMenu(), wxT("Edit"));
1131 menu
->Append(wxID_FIND
, wxT("Find..."));
1132 menu
->Append(wxID_FIND_NEXT
, wxT("Find next\tF3"));
1133 menu
->Append(wxID_FIND_PREV
, wxT("Find previous\tSHIFT+F3"));
1134 menu
->AppendSeparator();
1135 menu
->Append(wxID_CLEAR
, towxstring("Clear"));
1136 mb
->Append(menu
= new wxMenu(), wxT("Debug"));
1137 m_singlestep
= menu
->Append(wxID_SINGLESTEP
, towxstring("Singlestep\tF2"));
1138 menu
->Append(wxID_FRAMEADVANCE
, towxstring("Frame advance\tF4"));
1139 menu
->Append(wxID_CONTINUE
, towxstring("Continue\tF5"));
1140 menu
->AppendSeparator();
1141 menu
->Append(wxID_BREAKPOINTS
, towxstring("Breakpoints"));
1142 m_singlestep
->Enable(false);
1143 Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxwin_tracelog::on_menu
),
1146 wxSize tmp
= panel
->GetMinSize();
1147 panel
->SetMinSize(panel
->DoGetBestSize());
1148 top_s
->SetSizeHints(this);
1149 wxSize tmp2
= GetClientSize();
1150 panel
->SetMinSize(tmp
);
1151 top_s
->SetSizeHints(this);
1152 SetClientSize(tmp2
);
1158 std::string language
;
1162 class wxwin_disassembler
: public wxFrame
1165 wxwin_disassembler(wxWindow
* parent
);
1166 bool ShouldPreventAppExit() const { return false; }
1167 scroll_bar
* get_scroll() { return scroll
; }
1168 void on_menu(wxCommandEvent
& e
);
1169 void on_wclose(wxCloseEvent
& e
);
1171 class _panel
: public text_framebuffer_panel
1174 _panel(wxwin_disassembler
* parent
);
1175 void on_size(wxSizeEvent
& e
);
1176 void on_mouse(wxMouseEvent
& e
);
1177 wxSize
DoGetBestSize() const;
1179 std::vector
<uint64_t> rows
;
1180 std::map
<uint64_t, disasm_row
> row_map
;
1181 void on_popup_menu(wxCommandEvent
& e
);
1183 void prepare_paint();
1185 uint64_t pressed_row
;
1186 uint64_t current_row
;
1188 wxwin_disassembler
* p
;
1190 bool do_exit_save();
1191 void add_row(uint64_t addr
, const disasm_row
& row
, bool last
);
1192 void add_rows(const std::map
<uint64_t, disasm_row
>& rowdata
);
1193 void add_rows_main(const std::map
<uint64_t, disasm_row
>& rowdata
);
1194 void run_disassembler(const std::string
& disasm
, uint64_t addrbase
, uint64_t count
);
1195 void scroll_pane(uint64_t line
);
1202 wxwin_disassembler::_panel::_panel(wxwin_disassembler
* parent
)
1203 : text_framebuffer_panel(parent
, 20, 5, wxID_ANY
, NULL
)
1210 this->Connect(wxEVT_SIZE
, wxSizeEventHandler(wxwin_disassembler::_panel::on_size
), NULL
, this);
1211 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
,
1213 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
, this);
1214 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
, this);
1215 this->Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
,
1219 void wxwin_disassembler::_panel::on_size(wxSizeEvent
& e
)
1221 wxSize newsize
= e
.GetSize();
1222 auto tcell
= get_cell();
1223 size_t lines
= newsize
.y
/ tcell
.second
;
1224 size_t linelen
= newsize
.x
/ tcell
.first
;
1225 if(lines
< 1) lines
= 1;
1226 if(linelen
< 1) linelen
= 1;
1227 set_size(linelen
, lines
);
1228 p
->get_scroll()->set_page_size(lines
);
1233 void wxwin_disassembler::_panel::on_mouse(wxMouseEvent
& e
)
1235 uint64_t local_line
= pos
+ e
.GetY() / get_cell().second
;
1237 if(local_line
< rows
.size()) {
1239 pressed_row
= local_line
;
1241 }else if(e
.RightUp()) {
1244 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
,
1245 wxCommandEventHandler(wxwin_disassembler::_panel::on_popup_menu
), NULL
, this);
1246 menu
.Append(wxID_COPY
, wxT("Copy to clipboard"));
1247 menu
.Append(wxID_SAVE
, wxT("Save to file"));
1248 menu
.AppendSeparator();
1249 menu
.Append(wxID_DISASM_MORE
, wxT("Disassemble more"));
1250 menu
.AppendSeparator();
1251 menu
.Append(wxID_DELETE
, wxT("Delete"));
1254 current_row
= min(local_line
, static_cast<uint64_t>(rows
.size()));
1260 p
->get_scroll()->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
1263 wxSize
wxwin_disassembler::_panel::DoGetBestSize() const
1265 return wxSize(40 * 8, 25 * 16);
1268 void wxwin_disassembler::_panel::prepare_paint()
1270 p
->get_scroll()->set_range(rows
.size());
1271 uint64_t m
= min(pressed_row
, current_row
);
1272 uint64_t M
= max(pressed_row
, current_row
);
1273 auto s
= get_characters();
1275 for(i
= pos
; i
< pos
+ s
.second
&& i
< rows
.size(); i
++) {
1276 bool selected
= holding
&& (i
>= m
) && (i
<= M
);
1277 uint32_t fg
= selected
? 0x0000FF : 0x000000;
1278 uint32_t bg
= selected
? 0x000000 : 0xFFFFFF;
1279 write(row_map
[rows
[i
]].row
, s
.first
, 0, i
- pos
, fg
, bg
);
1281 for(; i
< pos
+ s
.second
; i
++) {
1282 write("", s
.first
, 0, i
- pos
, 0xFFFFFF, 0xFFFFFF);
1286 void wxwin_disassembler::on_menu(wxCommandEvent
& e
)
1288 if(e
.GetId() == wxID_EXIT
) {
1290 int r
= prompt_for_save(this, "Disassembly");
1291 if(r
< 0 || (r
> 0 && !do_exit_save()))
1296 } else if(e
.GetId() == wxID_SAVE
) {
1298 std::string filename
= choose_file_save(this, "Save disassembly to",
1299 lsnes_instance
.project
.otherpath(), filetype_disassembly
);
1300 std::ofstream
s(filename
, std::ios::app
);
1301 if(!s
) throw std::runtime_error("Error opening output file");
1302 for(auto& i
: panel
->rows
)
1303 s
<< panel
->row_map
[i
].row
<< std::endl
;
1304 if(!s
) throw std::runtime_error("Error writing output file");
1306 } catch(canceled_exception
& e
) {
1307 } catch(std::exception
& e
) {
1308 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1311 } else if(e
.GetId() == wxID_DISASM
) {
1313 dialog_disassemble
* d
= new dialog_disassemble(this);
1314 if(d
->ShowModal() != wxID_OK
) {
1318 std::string disasm
= d
->get_disassembler();
1319 uint64_t addr
= d
->get_address();
1320 uint64_t count
= d
->get_count();
1322 lsnes_instance
.iqueue
.run_async([this, disasm
, addr
, count
]() {
1323 this->run_disassembler(disasm
, addr
, count
);
1324 }, [](std::exception
& e
) {});
1325 } else if(e
.GetId() == wxID_GOTO
) {
1327 std::string to
= pick_text(this, "Goto", "Enter address to go to:", "");
1328 lsnes_instance
.iqueue
.run_async([this, to
]() {
1333 std::string _to
= to
;
1334 size_t sp
= _to
.find_first_of("+");
1335 if(sp
>= _to
.length()) {
1338 vma
= _to
.substr(0, sp
);
1339 offset
= _to
.substr(sp
+ 1);
1343 for(auto i
: lsnes_instance
.memory
.get_regions()) {
1344 if(i
->name
== vma
) {
1351 show_message_ok(this, "Error in address",
1352 "No such memory area known",
1353 wxICON_EXCLAMATION
);
1358 if(run_show_error(this, "Error in address", "Expected <hexdigits> or "
1359 " <name>+<hexdigits>", [&addr
, offset
]() {
1360 addr
= hex::from
<uint64_t>(offset
); }))
1363 runuifun([this, addr
]() {
1366 uint64_t high
= this->panel
->rows
.size();
1367 while(low
< high
&& low
< high
- 1) {
1368 nrow
= (low
+ high
) / 2;
1369 if(this->panel
->rows
[nrow
] > addr
)
1371 else if(this->panel
->rows
[nrow
] < addr
)
1376 this->scroll_pane(nrow
);
1378 }, [](std::exception
& e
) {});
1379 } catch(canceled_exception
& e
) {
1384 void remove_from_array(std::vector
<uint64_t>& v
, uint64_t e
)
1386 //Binary search for the element to remove.
1388 size_t high
= v
.size();
1391 mid
= (low
+ high
) / 2;
1400 v
.erase(v
.begin() + mid
);
1403 void wxwin_disassembler::_panel::on_popup_menu(wxCommandEvent
& e
)
1405 if(e
.GetId() == wxID_DISASM_MORE
)
1407 if(current_row
>= rows
.size())
1409 uint64_t base
= rows
[current_row
];
1410 uint64_t rbase
= base
;
1411 if(!row_map
.count(base
))
1413 auto& r
= row_map
[base
];
1414 base
= base
+ r
.cover
;
1415 std::string disasm
= r
.language
;
1416 dialog_disassemble
* d
= new dialog_disassemble(this, base
, disasm
);
1417 if(d
->ShowModal() != wxID_OK
) {
1421 disasm
= d
->get_disassembler();
1422 uint64_t addr
= d
->get_address();
1423 uint64_t count
= d
->get_count();
1426 lsnes_instance
.iqueue
.run_async([pp
, disasm
, addr
, count
]() {
1427 pp
->run_disassembler(disasm
, addr
, count
);
1428 }, [](std::exception
& e
) {});
1429 //Delete entries in (rbase, addr) if addr = base.
1431 for(uint64_t i
= rbase
+ 1; i
< addr
; i
++)
1432 if(row_map
.count(i
)) {
1433 //This line needs to be removed from rows too.
1435 remove_from_array(rows
, i
);
1440 uint64_t m
= min(min(pressed_row
, current_row
), (uint64_t)rows
.size());
1441 uint64_t M
= min(max(pressed_row
, current_row
) + 1, (uint64_t)rows
.size());
1444 for(uint64_t i
= m
; i
< M
; i
++) {
1446 std::string mline
= row_map
[rows
[i
]].row
;
1447 if(lines
== 1) str
+= "\n";
1449 if(lines
>= 1) str
+= "\n";
1457 if (wxTheClipboard
->Open()) {
1458 wxTheClipboard
->SetData(new wxTextDataObject(towxstring(str
)));
1459 wxTheClipboard
->Close();
1464 std::string filename
= choose_file_save(this, "Save disassembly fragment to",
1465 lsnes_instance
.project
.otherpath(), filetype_disassembly
);
1466 std::ofstream
s(filename
, std::ios::app
);
1467 if(!s
) throw std::runtime_error("Error opening output file");
1468 if(lines
== 1) str
+= "\n";
1470 if(!s
) throw std::runtime_error("Error writing output file");
1471 } catch(canceled_exception
& e
) {
1472 } catch(std::exception
& e
) {
1473 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1478 for(uint64_t i
= m
; i
< M
; i
++)
1479 row_map
.erase(rows
[i
]);
1480 rows
.erase(rows
.begin() + m
, rows
.begin() + M
);
1488 std::string
format_vma_offset(memory_region
& region
, uint64_t offset
)
1490 std::ostringstream y
;
1492 size_t sizedigits
= 0;
1493 uint64_t tmp
= region
.size
- 1;
1498 y
<< "+" << std::hex
<< std::setfill('0') << std::setw(sizedigits
) << offset
;
1502 std::string
lookup_address(uint64_t raw
)
1504 auto g
= lsnes_instance
.memory
.lookup(raw
);
1506 return hex::to
<uint64_t>(raw
);
1508 return format_vma_offset(*g
.first
, g
.second
);
1511 inline int sign_compare(uint64_t a
, uint64_t b
)
1513 if(a
< b
) return -1;
1518 void insert_into_array(std::vector
<uint64_t>& v
, uint64_t e
)
1520 //Binary search for the gap to insert to.
1522 size_t high
= v
.size();
1525 mid
= (low
+ high
) / 2;
1526 int s1
= sign_compare(v
[mid
], e
);
1527 int s2
= ((mid
+ 1) < v
.size()) ? sign_compare(v
[mid
+ 1], e
) : 1;
1528 if(s1
< 0 && s2
> 0)
1530 else if(s1
== 0 || s2
== 0)
1537 if(mid
< v
.size() && v
[mid
] < e
)
1539 v
.insert(v
.begin() + mid
, e
);
1542 void wxwin_disassembler::add_row(uint64_t addr
, const disasm_row
& row
, bool last
)
1544 auto& rows
= panel
->rows
;
1545 auto& row_map
= panel
->row_map
;
1546 if(row_map
.count(addr
)) {
1547 row_map
[addr
] = row
;
1549 //We need to insert the row into rows.
1550 row_map
[addr
] = row
;
1551 insert_into_array(rows
, addr
);
1555 for(uint64_t i
= addr
+ 1; i
< addr
+ row
.cover
; i
++)
1556 if(row_map
.count(i
)) {
1557 //This line needs to be removed from rows too.
1559 remove_from_array(rows
, i
);
1563 void wxwin_disassembler::add_rows(const std::map
<uint64_t, disasm_row
>& rowdata
)
1565 for(auto i
= rowdata
.begin(); i
!= rowdata
.end(); i
++) {
1568 bool last
= (j
== rowdata
.end());
1569 add_row(i
->first
, i
->second
, last
);
1571 panel
->request_paint();
1574 void wxwin_disassembler::add_rows_main(const std::map
<uint64_t, disasm_row
>& rowdata
)
1576 std::map
<uint64_t, disasm_row
> _rowdata
;
1577 for(auto& i
: rowdata
) {
1578 _rowdata
[i
.first
] = i
.second
;
1579 _rowdata
[i
.first
].row
= lookup_address(i
.first
) + " " + i
.second
.row
;
1581 runuifun([this, _rowdata
]() { this->add_rows(_rowdata
); });
1584 template<typename T
, bool hex
> disasm_row
_disassemble_data_item(uint64_t& addrbase
, int endian
,
1585 const std::string
& disasm
)
1587 char buf
[sizeof(T
)];
1588 for(size_t i
= 0; i
< sizeof(T
); i
++)
1589 buf
[i
] = lsnes_instance
.memory
.read
<uint8_t>(addrbase
+ i
);
1592 r
.row
= (stringfmt() << "DATA 0x" << hex::to
<T
>(serialization::read_endian
<T
>(buf
, endian
))).
1594 else if(sizeof(T
) > 1)
1595 r
.row
= (stringfmt() << "DATA " << serialization::read_endian
<T
>(buf
, endian
)).str();
1597 r
.row
= (stringfmt() << "DATA " << (int)serialization::read_endian
<T
>(buf
, endian
)).str();
1598 r
.cover
= sizeof(T
);
1599 r
.language
= disasm
;
1600 addrbase
+= sizeof(T
);
1604 disasm_row
disassemble_data_item(uint64_t& addrbase
, const std::string
& disasm
)
1607 if(disasm
[7] == 'l') endian
= -1;
1608 if(disasm
[7] == 'h') endian
= 0;
1609 if(disasm
[7] == 'b') endian
= 1;
1611 case 'b': return _disassemble_data_item
<int8_t, false>(addrbase
, endian
, disasm
);
1612 case 'B': return _disassemble_data_item
<uint8_t, false>(addrbase
, endian
, disasm
);
1613 case 'c': return _disassemble_data_item
<uint8_t, true>(addrbase
, endian
, disasm
);
1614 case 'C': return _disassemble_data_item
<uint16_t, true>(addrbase
, endian
, disasm
);
1615 case 'd': return _disassemble_data_item
<int32_t, false>(addrbase
, endian
, disasm
);
1616 case 'D': return _disassemble_data_item
<uint32_t, false>(addrbase
, endian
, disasm
);
1617 case 'f': return _disassemble_data_item
<float, false>(addrbase
, endian
, disasm
);
1618 case 'F': return _disassemble_data_item
<double, false>(addrbase
, endian
, disasm
);
1619 case 'h': return _disassemble_data_item
<ss_int24_t
, false>(addrbase
, endian
, disasm
);
1620 case 'H': return _disassemble_data_item
<ss_uint24_t
, false>(addrbase
, endian
, disasm
);
1621 case 'i': return _disassemble_data_item
<ss_uint24_t
, true>(addrbase
, endian
, disasm
);
1622 case 'I': return _disassemble_data_item
<uint32_t, true>(addrbase
, endian
, disasm
);
1623 case 'q': return _disassemble_data_item
<int64_t, false>(addrbase
, endian
, disasm
);
1624 case 'Q': return _disassemble_data_item
<uint64_t, false>(addrbase
, endian
, disasm
);
1625 case 'r': return _disassemble_data_item
<uint64_t, true>(addrbase
, endian
, disasm
);
1626 case 'w': return _disassemble_data_item
<int16_t, false>(addrbase
, endian
, disasm
);
1627 case 'W': return _disassemble_data_item
<uint16_t, false>(addrbase
, endian
, disasm
);
1629 throw std::runtime_error("Invalid kind of data");
1632 void wxwin_disassembler::scroll_pane(uint64_t line
)
1634 unsigned r
= panel
->get_characters().second
;
1635 unsigned offset
= r
/ 2;
1637 scroll
->set_position(panel
->pos
= 0);
1638 else if(line
+ r
< panel
->rows
.size())
1639 scroll
->set_position(panel
->pos
= line
- offset
);
1641 scroll
->set_position(panel
->pos
= panel
->rows
.size() - r
);
1642 panel
->request_paint();
1645 void wxwin_disassembler::on_wclose(wxCloseEvent
& e
)
1647 if(dirty
&& !wxwidgets_exiting
) {
1648 int r
= prompt_for_save(this, "Disassembly");
1649 if(r
< 0 || (r
> 0 && !do_exit_save()))
1657 void wxwin_disassembler::run_disassembler(const std::string
& disasm
, uint64_t addrbase
, uint64_t count
)
1659 std::map
<uint64_t, disasm_row
> rowdata
;
1660 if(regex_match("\\$data:.*", disasm
)) {
1661 if(run_show_error(this, "Error in disassember", "Error in disassember",
1662 [disasm
, &rowdata
, &addrbase
, count
]() {
1663 for(uint64_t i
= 0; i
< count
; i
++) {
1664 uint64_t base
= addrbase
;
1665 disasm_row r
= disassemble_data_item(addrbase
, disasm
);
1670 add_rows_main(rowdata
);
1674 if(run_show_error(this, "Error in disassember", "No disassembler '" + disasm
+ "' found",
1676 d
= &disassembler::byname(disasm
);
1679 for(uint64_t i
= 0; i
< count
; i
++) {
1680 uint64_t base
= addrbase
;
1682 r
.row
= d
->disassemble(addrbase
, [&addrbase
]() -> unsigned char {
1683 return lsnes_instance
.memory
.read
<uint8_t>(addrbase
++);
1685 r
.cover
= addrbase
- base
;
1686 r
.language
= disasm
;
1689 add_rows_main(rowdata
);
1692 bool wxwin_disassembler::do_exit_save()
1696 std::string filename
= choose_file_save(this, "Save disassembly to",
1697 lsnes_instance
.project
.otherpath(), filetype_disassembly
);
1698 std::ofstream
s(filename
, std::ios::app
);
1699 if(!s
) throw std::runtime_error("Error opening output file");
1700 for(auto& i
: panel
->rows
)
1701 s
<< panel
->row_map
[i
].row
<< std::endl
;
1702 if(!s
) throw std::runtime_error("Error writing output file");
1704 } catch(canceled_exception
& e
) {
1706 } catch(std::exception
& e
) {
1707 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1714 wxwin_disassembler::wxwin_disassembler(wxWindow
* parent
)
1715 : wxFrame(parent
, wxID_ANY
, towxstring("lsnes: Disassembler"), wxDefaultPosition
,
1716 wxDefaultSize
, wxMINIMIZE_BOX
| wxRESIZE_BORDER
| wxSYSTEM_MENU
| wxCAPTION
| wxCLOSE_BOX
|
1721 wxBoxSizer
* top_s
= new wxBoxSizer(wxHORIZONTAL
);
1723 top_s
->Add(panel
= new _panel(this), 1, wxGROW
);
1724 top_s
->Add(scroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
1725 scroll
->set_page_size(panel
->get_characters().second
);
1726 scroll
->set_handler([this](scroll_bar
& s
) {
1727 this->panel
->pos
= s
.get_position();
1728 this->panel
->request_paint();
1734 SetMenuBar(mb
= new wxMenuBar
);
1735 SetStatusBar(sb
= new wxStatusBar(this));
1736 mb
->Append(menu
= new wxMenu(), wxT("File"));
1737 menu
->Append(wxID_DISASM
, wxT("Disassemble..."));
1738 menu
->AppendSeparator();
1739 menu
->Append(wxID_SAVE
, wxT("Save"));
1740 menu
->AppendSeparator();
1741 menu
->Append(wxID_EXIT
, wxT("Close"));
1742 Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxwin_disassembler::on_menu
),
1744 mb
->Append(menu
= new wxMenu(), wxT("Edit"));
1745 menu
->Append(wxID_GOTO
, wxT("Goto"));
1747 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_disassembler::on_wclose
),
1750 wxSize tmp
= panel
->GetMinSize();
1751 panel
->SetMinSize(panel
->DoGetBestSize());
1752 top_s
->SetSizeHints(this);
1753 wxSize tmp2
= GetClientSize();
1754 panel
->SetMinSize(tmp
);
1755 top_s
->SetSizeHints(this);
1756 SetClientSize(tmp2
);
1759 dialog_breakpoints::dialog_breakpoints(wxwin_tracelog
* parent
)
1760 : wxDialog(parent
, wxID_ANY
, wxT("Breakpoints"))
1763 regions
= lsnes_instance
.memory
.get_regions();
1764 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
1766 top_s
->Add(brklist
= new wxListBox(this, wxID_ANY
, wxDefaultPosition
, wxSize(300, 400)), 1, wxGROW
);
1767 brklist
->Connect(wxEVT_COMMAND_LISTBOX_SELECTED
,
1768 wxCommandEventHandler(dialog_breakpoints::on_selchange
), NULL
, this);
1769 populate_breakpoints();
1770 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
1771 pbutton_s
->Add(addb
= new wxButton(this, wxID_ANY
, wxT("Add")), 0, wxGROW
);
1772 pbutton_s
->Add(delb
= new wxButton(this, wxID_ANY
, wxT("Remove")), 0, wxGROW
);
1773 pbutton_s
->AddStretchSpacer();
1774 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("Close")), 0, wxGROW
);
1775 delb
->Enable(false);
1776 addb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_add
), NULL
,
1778 delb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_delete
),
1780 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_ok
), NULL
,
1782 top_s
->Add(pbutton_s
, 0, wxGROW
);
1783 top_s
->SetSizeHints(this);
1787 void dialog_breakpoints::populate_breakpoints()
1789 auto t
= pwin
->get_breakpoints();
1791 std::string line
= format_line(i
);
1792 unsigned insert_pos
= get_insert_pos(i
);
1793 brklist
->Insert(towxstring(line
), insert_pos
);
1794 listsyms
.insert(listsyms
.begin() + insert_pos
, i
);
1798 void dialog_breakpoints::on_add(wxCommandEvent
& e
)
1801 debug_context::etype dtype
;
1802 dialog_breakpoint_add
* d
= new dialog_breakpoint_add(this, regions
);
1803 if(d
->ShowModal() != wxID_OK
) {
1807 rpair(addr
, dtype
) = d
->get_result();
1809 lsnes_instance
.iqueue
.run_async([this, addr
, dtype
]() {
1810 pwin
->add_breakpoint(addr
, dtype
);
1811 }, [](std::exception
& e
) {});
1812 auto ent
= std::make_pair(addr
, dtype
);
1813 std::string line
= format_line(ent
);
1814 unsigned insert_pos
= get_insert_pos(ent
);
1815 brklist
->Insert(towxstring(line
), insert_pos
);
1816 listsyms
.insert(listsyms
.begin() + insert_pos
, ent
);
1819 void dialog_breakpoints::on_delete(wxCommandEvent
& e
)
1821 int idx
= brklist
->GetSelection();
1822 if(idx
== wxNOT_FOUND
)
1825 debug_context::etype dtype
;
1826 addr
= listsyms
[idx
].first
;
1827 dtype
= listsyms
[idx
].second
;
1828 lsnes_instance
.iqueue
.run_async([this, addr
, dtype
]() {
1829 pwin
->remove_breakpoint(addr
, dtype
);
1830 }, [](std::exception
& e
) {});
1831 brklist
->Delete(idx
);
1832 listsyms
.erase(listsyms
.begin() + idx
);
1835 size_t dialog_breakpoints::get_insert_pos(std::pair
<uint64_t, debug_context::etype
> entry
)
1838 for(i
= 0; i
< listsyms
.size(); i
++)
1839 if(entry
< listsyms
[i
])
1844 std::string
dialog_breakpoints::format_line(std::pair
<uint64_t, debug_context::etype
> entry
)
1846 std::string base
= "";
1847 for(auto i
: regions
) {
1848 if(entry
.first
>= i
->base
&& entry
.first
< i
->base
+ i
->size
) {
1849 base
= format_vma_offset(*i
, entry
.first
- i
->base
);
1854 base
= hex::to
<uint64_t>(entry
.first
);
1855 if(entry
.second
== debug_context::DEBUG_READ
)
1856 return base
+ ": Read";
1857 if(entry
.second
== debug_context::DEBUG_WRITE
)
1858 return base
+ ": Write";
1859 if(entry
.second
== debug_context::DEBUG_EXEC
)
1860 return base
+ ": Execute";
1861 return base
+ ": Unknown";
1864 void dialog_breakpoints::on_selchange(wxCommandEvent
& e
)
1866 delb
->Enable(brklist
->GetSelection() != wxNOT_FOUND
);
1870 void wxeditor_tracelog_display(wxWindow
* parent
, int cpuid
, const std::string
& cpuname
)
1873 wxwin_tracelog
* d
= new wxwin_tracelog(parent
, cpuid
, cpuname
);
1875 } catch(std::exception
& e
) {
1876 show_message_ok(parent
, "Error opening trace logger", e
.what(), wxICON_EXCLAMATION
);
1880 void wxeditor_disassembler_display(wxWindow
* parent
)
1882 wxwin_disassembler
* d
= new wxwin_disassembler(parent
);