Tracelogger: Breakpoints
This commit is contained in:
parent
bad1141c65
commit
1994af1d09
7 changed files with 460 additions and 38 deletions
|
@ -21,13 +21,13 @@ extern const uint64_t debug_all_addr;
|
||||||
|
|
||||||
debug_handle debug_add_callback(uint64_t addr, debug_type type,
|
debug_handle debug_add_callback(uint64_t addr, debug_type type,
|
||||||
std::function<void(uint64_t addr, uint64_t value)> fn, std::function<void()> dtor);
|
std::function<void(uint64_t addr, uint64_t value)> fn, std::function<void()> dtor);
|
||||||
debug_handle debug_add_trace_callback(uint64_t proc, std::function<void(uint64_t proc, const char* str)> fn,
|
debug_handle debug_add_trace_callback(uint64_t proc, std::function<void(uint64_t proc, const char* str,
|
||||||
std::function<void()> dtor);
|
bool true_insn)> fn, std::function<void()> dtor);
|
||||||
void debug_remove_callback(uint64_t addr, debug_type type, debug_handle handle);
|
void debug_remove_callback(uint64_t addr, debug_type type, debug_handle handle);
|
||||||
void debug_fire_callback_read(uint64_t addr, uint64_t value);
|
void debug_fire_callback_read(uint64_t addr, uint64_t value);
|
||||||
void debug_fire_callback_write(uint64_t addr, uint64_t value);
|
void debug_fire_callback_write(uint64_t addr, uint64_t value);
|
||||||
void debug_fire_callback_exec(uint64_t addr, uint64_t value);
|
void debug_fire_callback_exec(uint64_t addr, uint64_t value);
|
||||||
void debug_fire_callback_trace(uint64_t proc, const char* str);
|
void debug_fire_callback_trace(uint64_t proc, const char* str, bool true_insn = true);
|
||||||
void debug_set_cheat(uint64_t addr, uint64_t value);
|
void debug_set_cheat(uint64_t addr, uint64_t value);
|
||||||
void debug_clear_cheat(uint64_t addr);
|
void debug_clear_cheat(uint64_t addr);
|
||||||
void debug_setxmask(uint64_t mask);
|
void debug_setxmask(uint64_t mask);
|
||||||
|
@ -35,5 +35,6 @@ void debug_tracelog(uint64_t proc, const std::string& filename);
|
||||||
bool debug_tracelogging(uint64_t proc);
|
bool debug_tracelogging(uint64_t proc);
|
||||||
void debug_set_tracelog_change_cb(std::function<void()> cb);
|
void debug_set_tracelog_change_cb(std::function<void()> cb);
|
||||||
void debug_core_change();
|
void debug_core_change();
|
||||||
|
void debug_request_break();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,6 +30,8 @@ void switch_projects(const std::string& newproj);
|
||||||
void close_rom();
|
void close_rom();
|
||||||
void load_new_rom(const romload_request& req);
|
void load_new_rom(const romload_request& req);
|
||||||
void reload_current_rom();
|
void reload_current_rom();
|
||||||
|
void do_break_pause();
|
||||||
|
void convert_break_to_pause();
|
||||||
|
|
||||||
extern settingvar::variable<settingvar::model_bool<settingvar::yes_no>> jukebox_dflt_binary;
|
extern settingvar::variable<settingvar::model_bool<settingvar::yes_no>> jukebox_dflt_binary;
|
||||||
extern settingvar::variable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary;
|
extern settingvar::variable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "core/command.hpp"
|
#include "core/command.hpp"
|
||||||
#include "core/debug.hpp"
|
#include "core/debug.hpp"
|
||||||
#include "core/dispatch.hpp"
|
#include "core/dispatch.hpp"
|
||||||
|
#include "core/mainloop.hpp"
|
||||||
#include "core/moviedata.hpp"
|
#include "core/moviedata.hpp"
|
||||||
#include "library/directory.hpp"
|
#include "library/directory.hpp"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
@ -18,7 +19,7 @@ namespace
|
||||||
};
|
};
|
||||||
struct cb_trace
|
struct cb_trace
|
||||||
{
|
{
|
||||||
std::function<void(uint64_t proc, const char* str)> cb;
|
std::function<void(uint64_t proc, const char* str, bool true_insn)> cb;
|
||||||
std::function<void()> dtor;
|
std::function<void()> dtor;
|
||||||
};
|
};
|
||||||
typedef std::list<cb_rwx> cb_list;
|
typedef std::list<cb_rwx> cb_list;
|
||||||
|
@ -33,6 +34,7 @@ namespace
|
||||||
std::function<void()> tracelog_change_cb;
|
std::function<void()> tracelog_change_cb;
|
||||||
struct dispatch::target<> corechange;
|
struct dispatch::target<> corechange;
|
||||||
bool corechange_r = false;
|
bool corechange_r = false;
|
||||||
|
bool requesting_break = false;
|
||||||
|
|
||||||
struct tracelog_file
|
struct tracelog_file
|
||||||
{
|
{
|
||||||
|
@ -125,8 +127,8 @@ debug_handle debug_add_callback(uint64_t addr, debug_type type, std::function<vo
|
||||||
return _debug_add_callback(cb, addr, type, t, dtor);
|
return _debug_add_callback(cb, addr, type, t, dtor);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_handle debug_add_trace_callback(uint64_t proc, std::function<void(uint64_t proc, const char* str)> fn,
|
debug_handle debug_add_trace_callback(uint64_t proc, std::function<void(uint64_t proc, const char* str,
|
||||||
std::function<void()> dtor)
|
bool true_insn)> fn, std::function<void()> dtor)
|
||||||
{
|
{
|
||||||
cb_trace t;
|
cb_trace t;
|
||||||
t.cb = fn;
|
t.cb = fn;
|
||||||
|
@ -145,33 +147,45 @@ void debug_remove_callback(uint64_t addr, debug_type type, debug_handle handle)
|
||||||
|
|
||||||
void debug_fire_callback_read(uint64_t addr, uint64_t value)
|
void debug_fire_callback_read(uint64_t addr, uint64_t value)
|
||||||
{
|
{
|
||||||
|
requesting_break = false;
|
||||||
cb_list* cb1 = read_cb.count(debug_all_addr) ? &read_cb[debug_all_addr] : &dummy_cb;
|
cb_list* cb1 = read_cb.count(debug_all_addr) ? &read_cb[debug_all_addr] : &dummy_cb;
|
||||||
cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
|
cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
|
||||||
for(auto& i : *cb1) i.cb(addr, value);
|
for(auto& i : *cb1) i.cb(addr, value);
|
||||||
for(auto& i : *cb2) i.cb(addr, value);
|
for(auto& i : *cb2) i.cb(addr, value);
|
||||||
|
if(requesting_break)
|
||||||
|
do_break_pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_fire_callback_write(uint64_t addr, uint64_t value)
|
void debug_fire_callback_write(uint64_t addr, uint64_t value)
|
||||||
{
|
{
|
||||||
|
requesting_break = false;
|
||||||
cb_list* cb1 = write_cb.count(debug_all_addr) ? &write_cb[debug_all_addr] : &dummy_cb;
|
cb_list* cb1 = write_cb.count(debug_all_addr) ? &write_cb[debug_all_addr] : &dummy_cb;
|
||||||
cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
|
cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
|
||||||
for(auto& i : *cb1) i.cb(addr, value);
|
for(auto& i : *cb1) i.cb(addr, value);
|
||||||
for(auto& i : *cb2) i.cb(addr, value);
|
for(auto& i : *cb2) i.cb(addr, value);
|
||||||
|
if(requesting_break)
|
||||||
|
do_break_pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_fire_callback_exec(uint64_t addr, uint64_t value)
|
void debug_fire_callback_exec(uint64_t addr, uint64_t value)
|
||||||
{
|
{
|
||||||
|
requesting_break = false;
|
||||||
cb_list* cb1 = exec_cb.count(debug_all_addr) ? &exec_cb[debug_all_addr] : &dummy_cb;
|
cb_list* cb1 = exec_cb.count(debug_all_addr) ? &exec_cb[debug_all_addr] : &dummy_cb;
|
||||||
cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
|
cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
|
||||||
if(value & xmask)
|
if(value & xmask)
|
||||||
for(auto& i : *cb1) i.cb(addr, value);
|
for(auto& i : *cb1) i.cb(addr, value);
|
||||||
for(auto& i : *cb2) i.cb(addr, value);
|
for(auto& i : *cb2) i.cb(addr, value);
|
||||||
|
if(requesting_break)
|
||||||
|
do_break_pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_fire_callback_trace(uint64_t proc, const char* str)
|
void debug_fire_callback_trace(uint64_t proc, const char* str, bool true_insn)
|
||||||
{
|
{
|
||||||
|
requesting_break = false;
|
||||||
cb2_list* cb = trace_cb.count(proc) ? &trace_cb[proc] : &dummy_cb2;
|
cb2_list* cb = trace_cb.count(proc) ? &trace_cb[proc] : &dummy_cb2;
|
||||||
for(auto& i : *cb) i.cb(proc, str);
|
for(auto& i : *cb) i.cb(proc, str, true_insn);
|
||||||
|
if(requesting_break)
|
||||||
|
do_break_pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_set_cheat(uint64_t addr, uint64_t value)
|
void debug_set_cheat(uint64_t addr, uint64_t value)
|
||||||
|
@ -226,7 +240,7 @@ void debug_tracelog(uint64_t proc, const std::string& filename)
|
||||||
throw std::runtime_error("Can't open '" + full_filename + "'");
|
throw std::runtime_error("Can't open '" + full_filename + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace_outputs[proc].second = debug_add_trace_callback(proc, [](uint64_t proc, const char* str) {
|
trace_outputs[proc].second = debug_add_trace_callback(proc, [](uint64_t proc, const char* str, bool dummy) {
|
||||||
if(!trace_outputs.count(proc)) return;
|
if(!trace_outputs.count(proc)) return;
|
||||||
trace_outputs[proc].first->stream << str << std::endl;
|
trace_outputs[proc].first->stream << str << std::endl;
|
||||||
}, [proc]() { debug_tracelog(proc, ""); });
|
}, [proc]() { debug_tracelog(proc, ""); });
|
||||||
|
@ -253,6 +267,11 @@ void debug_core_change()
|
||||||
kill_hooks(trace_cb);
|
kill_hooks(trace_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void debug_request_break()
|
||||||
|
{
|
||||||
|
requesting_break = true;
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
command::fnptr<> callbacks_show(lsnes_cmd, "show-callbacks", "", "",
|
command::fnptr<> callbacks_show(lsnes_cmd, "show-callbacks", "", "",
|
||||||
|
|
|
@ -81,6 +81,7 @@ namespace
|
||||||
bool cancel_advance;
|
bool cancel_advance;
|
||||||
//Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
|
//Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
|
||||||
enum advance_mode amode;
|
enum advance_mode amode;
|
||||||
|
enum advance_mode old_mode;
|
||||||
//Mode and filename of pending load, one of LOAD_* constants.
|
//Mode and filename of pending load, one of LOAD_* constants.
|
||||||
bool load_paused;
|
bool load_paused;
|
||||||
int loadmode;
|
int loadmode;
|
||||||
|
@ -104,8 +105,9 @@ namespace
|
||||||
//Macro hold.
|
//Macro hold.
|
||||||
bool macro_hold_1;
|
bool macro_hold_1;
|
||||||
bool macro_hold_2;
|
bool macro_hold_2;
|
||||||
|
//In break pause.
|
||||||
|
bool break_pause = false;
|
||||||
|
|
||||||
enum advance_mode old_mode;
|
|
||||||
|
|
||||||
std::string save_jukebox_name(size_t i)
|
std::string save_jukebox_name(size_t i)
|
||||||
{
|
{
|
||||||
|
@ -252,6 +254,8 @@ namespace
|
||||||
//Do pending load (automatically unpauses).
|
//Do pending load (automatically unpauses).
|
||||||
void mark_pending_load(std::string filename, int lmode)
|
void mark_pending_load(std::string filename, int lmode)
|
||||||
{
|
{
|
||||||
|
if(break_pause)
|
||||||
|
break_pause = false;
|
||||||
loadmode = lmode;
|
loadmode = lmode;
|
||||||
pending_load = filename;
|
pending_load = filename;
|
||||||
old_mode = amode;
|
old_mode = amode;
|
||||||
|
@ -327,6 +331,12 @@ void update_movie_state()
|
||||||
_status.set("!subframe", "N/A");
|
_status.set("!subframe", "N/A");
|
||||||
}
|
}
|
||||||
_status.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
|
_status.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
|
||||||
|
if(break_pause)
|
||||||
|
_status.set("!pause", "B");
|
||||||
|
else if(amode == ADVANCE_PAUSE)
|
||||||
|
_status.set("!pause", "P");
|
||||||
|
else
|
||||||
|
_status.set("!pause", "");
|
||||||
if(movb) {
|
if(movb) {
|
||||||
auto& mo = movb.get_movie();
|
auto& mo = movb.get_movie();
|
||||||
readonly = mo.readonly_mode();
|
readonly = mo.readonly_mode();
|
||||||
|
@ -552,26 +562,25 @@ namespace
|
||||||
command::fnptr<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
|
command::fnptr<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
|
||||||
"Syntax: unpause-emulator\nUnpauses the emulator.\n",
|
"Syntax: unpause-emulator\nUnpauses the emulator.\n",
|
||||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||||
|
break_pause = false;
|
||||||
amode = ADVANCE_AUTO;
|
amode = ADVANCE_AUTO;
|
||||||
platform::set_paused(false);
|
platform::set_paused(false);
|
||||||
platform::cancel_wait();
|
platform::cancel_wait();
|
||||||
messages << "Unpaused" << std::endl;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
command::fnptr<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
|
command::fnptr<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
|
||||||
"Syntax: pause-emulator\n(Un)pauses the emulator.\n",
|
"Syntax: pause-emulator\n(Un)pauses the emulator.\n",
|
||||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||||
if(amode != ADVANCE_AUTO) {
|
if(amode != ADVANCE_AUTO || break_pause) {
|
||||||
|
break_pause = false;
|
||||||
amode = ADVANCE_AUTO;
|
amode = ADVANCE_AUTO;
|
||||||
platform::set_paused(false);
|
platform::set_paused(false);
|
||||||
platform::cancel_wait();
|
platform::cancel_wait();
|
||||||
messages << "Unpaused" << std::endl;
|
|
||||||
} else {
|
} else {
|
||||||
platform::cancel_wait();
|
platform::cancel_wait();
|
||||||
cancel_advance = false;
|
cancel_advance = false;
|
||||||
stop_at_frame_active = false;
|
stop_at_frame_active = false;
|
||||||
amode = ADVANCE_PAUSE;
|
amode = ADVANCE_PAUSE;
|
||||||
messages << "Paused" << std::endl;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -666,6 +675,8 @@ namespace
|
||||||
command::fnptr<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
|
command::fnptr<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
|
||||||
"Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
|
"Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
|
||||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||||
|
if(break_pause)
|
||||||
|
break_pause = false;
|
||||||
amode = ADVANCE_FRAME;
|
amode = ADVANCE_FRAME;
|
||||||
cancel_advance = false;
|
cancel_advance = false;
|
||||||
advanced_once = false;
|
advanced_once = false;
|
||||||
|
@ -684,6 +695,8 @@ namespace
|
||||||
command::fnptr<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
|
command::fnptr<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
|
||||||
"Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
|
"Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
|
||||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||||
|
if(break_pause)
|
||||||
|
break_pause = false;
|
||||||
amode = ADVANCE_SUBFRAME;
|
amode = ADVANCE_SUBFRAME;
|
||||||
cancel_advance = false;
|
cancel_advance = false;
|
||||||
advanced_once = false;
|
advanced_once = false;
|
||||||
|
@ -694,6 +707,8 @@ namespace
|
||||||
command::fnptr<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
|
command::fnptr<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
|
||||||
"No help available\n",
|
"No help available\n",
|
||||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||||
|
if(break_pause)
|
||||||
|
break_pause = false;
|
||||||
cancel_advance = true;
|
cancel_advance = true;
|
||||||
platform::cancel_wait();
|
platform::cancel_wait();
|
||||||
platform::set_paused(false);
|
platform::set_paused(false);
|
||||||
|
@ -702,6 +717,8 @@ namespace
|
||||||
command::fnptr<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
|
command::fnptr<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
|
||||||
"Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
|
"Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
|
||||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||||
|
if(break_pause)
|
||||||
|
break_pause = false;
|
||||||
amode = ADVANCE_SKIPLAG_PENDING;
|
amode = ADVANCE_SKIPLAG_PENDING;
|
||||||
platform::cancel_wait();
|
platform::cancel_wait();
|
||||||
platform::set_paused(false);
|
platform::set_paused(false);
|
||||||
|
@ -1319,3 +1336,22 @@ void close_rom()
|
||||||
mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
|
mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_break_pause()
|
||||||
|
{
|
||||||
|
break_pause = true;
|
||||||
|
update_movie_state();
|
||||||
|
while(break_pause) {
|
||||||
|
platform::set_paused(true);
|
||||||
|
platform::flush_command_queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void convert_break_to_pause()
|
||||||
|
{
|
||||||
|
if(break_pause) {
|
||||||
|
amode = ADVANCE_PAUSE;
|
||||||
|
break_pause = false;
|
||||||
|
update_movie_state();
|
||||||
|
}
|
||||||
|
}
|
|
@ -285,12 +285,13 @@ void handle_registerX(lua::state& L, uint64_t addr, int lfn)
|
||||||
D->_dtor(LL->handle());
|
D->_dtor(LL->handle());
|
||||||
});
|
});
|
||||||
else
|
else
|
||||||
D->h = debug_add_trace_callback(addr, [LL, D2](uint64_t proc, const char* str) {
|
D->h = debug_add_trace_callback(addr, [LL, D2](uint64_t proc, const char* str, bool true_insn) {
|
||||||
LL->pushlightuserdata(D2);
|
LL->pushlightuserdata(D2);
|
||||||
LL->rawget(LUA_REGISTRYINDEX);
|
LL->rawget(LUA_REGISTRYINDEX);
|
||||||
LL->pushnumber(proc);
|
LL->pushnumber(proc);
|
||||||
LL->pushstring(str);
|
LL->pushstring(str);
|
||||||
do_lua_error(*LL, LL->pcall(2, 0, 0));
|
LL->pushboolean(true_insn);
|
||||||
|
do_lua_error(*LL, LL->pcall(3, 0, 0));
|
||||||
}, [LL, D]() {
|
}, [LL, D]() {
|
||||||
LL->pushlightuserdata(&D->addr);
|
LL->pushlightuserdata(&D->addr);
|
||||||
LL->pushnil();
|
LL->pushnil();
|
||||||
|
|
|
@ -1273,6 +1273,10 @@ void wxwin_mainwindow::update_statusbar(const std::map<std::string, std::u32stri
|
||||||
s << U" [" << read_variable_map(vars, "!saveslotinfo") << U"]";
|
s << U" [" << read_variable_map(vars, "!saveslotinfo") << U"]";
|
||||||
s << U" Speed: " << read_variable_map(vars, "!speed") << U"%";
|
s << U" Speed: " << read_variable_map(vars, "!speed") << U"%";
|
||||||
s << U" ";
|
s << U" ";
|
||||||
|
if(read_variable_map(vars, "!pause") == U"B")
|
||||||
|
s << U" Breakpoint";
|
||||||
|
else if(read_variable_map(vars, "!pause") == U"P")
|
||||||
|
s << U" Paused";
|
||||||
if(read_variable_map(vars, "!dumping") != U"")
|
if(read_variable_map(vars, "!dumping") != U"")
|
||||||
s << U" Dumping";
|
s << U" Dumping";
|
||||||
if(read_variable_map(vars, "!mode") == U"C")
|
if(read_variable_map(vars, "!mode") == U"C")
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
#include "platform/wxwidgets/textrender.hpp"
|
#include "platform/wxwidgets/textrender.hpp"
|
||||||
#include "platform/wxwidgets/scrollbar.hpp"
|
#include "platform/wxwidgets/scrollbar.hpp"
|
||||||
#include "platform/wxwidgets/loadsave.hpp"
|
#include "platform/wxwidgets/loadsave.hpp"
|
||||||
|
#include "core/command.hpp"
|
||||||
#include "core/debug.hpp"
|
#include "core/debug.hpp"
|
||||||
|
#include "core/mainloop.hpp"
|
||||||
#include "core/memorymanip.hpp"
|
#include "core/memorymanip.hpp"
|
||||||
#include "core/project.hpp"
|
#include "core/project.hpp"
|
||||||
#include "interface/disassembler.hpp"
|
#include "interface/disassembler.hpp"
|
||||||
|
@ -15,6 +17,7 @@
|
||||||
#include <wx/menu.h>
|
#include <wx/menu.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/listbox.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/combobox.h>
|
#include <wx/combobox.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
|
@ -32,6 +35,10 @@ namespace
|
||||||
wxID_GOTO,
|
wxID_GOTO,
|
||||||
wxID_DISASM,
|
wxID_DISASM,
|
||||||
wxID_DISASM_MORE,
|
wxID_DISASM_MORE,
|
||||||
|
wxID_SINGLESTEP,
|
||||||
|
wxID_BREAKPOINTS,
|
||||||
|
wxID_CONTINUE,
|
||||||
|
wxID_FRAMEADVANCE,
|
||||||
};
|
};
|
||||||
|
|
||||||
int prompt_for_save(wxWindow* parent, const std::string& what)
|
int prompt_for_save(wxWindow* parent, const std::string& what)
|
||||||
|
@ -377,6 +384,123 @@ namespace
|
||||||
ok->Enable(is_ok);
|
ok->Enable(is_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class wxwin_tracelog;
|
||||||
|
|
||||||
|
class dialog_breakpoint_add : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
dialog_breakpoint_add(wxWindow* parent, std::list<memory_region*> regions);
|
||||||
|
std::pair<uint64_t, debug_type> get_result();
|
||||||
|
void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
|
||||||
|
void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
|
||||||
|
void on_address_change(wxCommandEvent& e);
|
||||||
|
private:
|
||||||
|
std::list<memory_region*> regions;
|
||||||
|
wxComboBox* vmasel;
|
||||||
|
wxTextCtrl* address;
|
||||||
|
wxComboBox* typesel;
|
||||||
|
wxButton* ok;
|
||||||
|
wxButton* cancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog_breakpoint_add::dialog_breakpoint_add(wxWindow* parent, std::list<memory_region*> _regions)
|
||||||
|
: wxDialog(parent, wxID_ANY, "Add breakpoint")
|
||||||
|
{
|
||||||
|
regions = _regions;
|
||||||
|
wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
|
||||||
|
SetSizer(top_s);
|
||||||
|
|
||||||
|
top_s->Add(new wxStaticText(this, wxID_ANY, "Memory region:"), 0, wxGROW);
|
||||||
|
top_s->Add(vmasel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
|
||||||
|
0, NULL, wxCB_READONLY), 1, wxGROW);
|
||||||
|
vmasel->Append(towxstring(""));
|
||||||
|
for(auto i : regions)
|
||||||
|
vmasel->Append(towxstring(i->name));
|
||||||
|
vmasel->SetSelection(0);
|
||||||
|
|
||||||
|
top_s->Add(new wxStaticText(this, wxID_ANY, "Offset (hexadecimal):"), 0, wxGROW);
|
||||||
|
top_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(350, -1)), 0,
|
||||||
|
wxGROW);
|
||||||
|
|
||||||
|
top_s->Add(new wxStaticText(this, wxID_ANY, "Breakpoint type:"), 0, wxGROW);
|
||||||
|
top_s->Add(typesel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
|
||||||
|
0, NULL, wxCB_READONLY), 1, wxGROW);
|
||||||
|
typesel->Append(towxstring("Read"));
|
||||||
|
typesel->Append(towxstring("Write"));
|
||||||
|
typesel->Append(towxstring("Execute"));
|
||||||
|
typesel->SetSelection(0);
|
||||||
|
|
||||||
|
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
pbutton_s->AddStretchSpacer();
|
||||||
|
pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
|
||||||
|
pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
|
||||||
|
ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_ok), NULL,
|
||||||
|
this);
|
||||||
|
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_cancel),
|
||||||
|
NULL, this);
|
||||||
|
top_s->Add(pbutton_s, 0, wxGROW);
|
||||||
|
top_s->SetSizeHints(this);
|
||||||
|
Fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_breakpoint_add::on_address_change(wxCommandEvent& e)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
hex::from<uint64_t>(tostdstring(address->GetValue()));
|
||||||
|
ok->Enable(true);
|
||||||
|
} catch(...) {
|
||||||
|
ok->Enable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint64_t, debug_type> dialog_breakpoint_add::get_result()
|
||||||
|
{
|
||||||
|
std::string vmaname = tostdstring(vmasel->GetStringSelection());
|
||||||
|
std::string addrtext = tostdstring(address->GetValue());
|
||||||
|
uint64_t base = 0;
|
||||||
|
if(vmaname != "") {
|
||||||
|
for(auto i : regions)
|
||||||
|
if(i->name == vmaname)
|
||||||
|
base = i->base;
|
||||||
|
}
|
||||||
|
uint64_t addr;
|
||||||
|
try {
|
||||||
|
addr = base + hex::from<uint64_t>(addrtext);
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
addr = base;
|
||||||
|
}
|
||||||
|
debug_type dtype = DEBUG_EXEC;
|
||||||
|
if(typesel->GetSelection() == 0)
|
||||||
|
dtype = DEBUG_READ;
|
||||||
|
if(typesel->GetSelection() == 1)
|
||||||
|
dtype = DEBUG_WRITE;
|
||||||
|
if(typesel->GetSelection() == 2)
|
||||||
|
dtype = DEBUG_EXEC;
|
||||||
|
return std::make_pair(addr, dtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class dialog_breakpoints : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
dialog_breakpoints(wxwin_tracelog* parent);
|
||||||
|
void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
|
||||||
|
void on_add(wxCommandEvent& e);
|
||||||
|
void on_delete(wxCommandEvent& e);
|
||||||
|
void on_selchange(wxCommandEvent& e);
|
||||||
|
private:
|
||||||
|
std::string format_line(std::pair<uint64_t, debug_type> entry);
|
||||||
|
size_t get_insert_pos(std::pair<uint64_t, debug_type> entry);
|
||||||
|
void populate_breakpoints();
|
||||||
|
std::list<memory_region*> regions;
|
||||||
|
wxButton* ok;
|
||||||
|
wxButton* addb;
|
||||||
|
wxButton* delb;
|
||||||
|
wxListBox* brklist;
|
||||||
|
wxwin_tracelog* pwin;
|
||||||
|
std::vector<std::pair<uint64_t, debug_type>> listsyms;
|
||||||
|
};
|
||||||
|
|
||||||
class wxwin_tracelog : public wxFrame
|
class wxwin_tracelog : public wxFrame
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -389,6 +513,9 @@ namespace
|
||||||
void on_menu(wxCommandEvent& e);
|
void on_menu(wxCommandEvent& e);
|
||||||
void process_lines();
|
void process_lines();
|
||||||
uint64_t get_find_line() { return find_active ? find_line : 0xFFFFFFFFFFFFFFFFULL; }
|
uint64_t get_find_line() { return find_active ? find_line : 0xFFFFFFFFFFFFFFFFULL; }
|
||||||
|
std::set<std::pair<uint64_t, debug_type>> get_breakpoints();
|
||||||
|
void add_breakpoint(uint64_t addr, debug_type dtype);
|
||||||
|
void remove_breakpoint(uint64_t addr, debug_type dtype);
|
||||||
private:
|
private:
|
||||||
class _panel : public text_framebuffer_panel
|
class _panel : public text_framebuffer_panel
|
||||||
{
|
{
|
||||||
|
@ -400,6 +527,7 @@ namespace
|
||||||
uint64_t pos;
|
uint64_t pos;
|
||||||
std::vector<std::string> rows;
|
std::vector<std::string> rows;
|
||||||
void on_popup_menu(wxCommandEvent& e);
|
void on_popup_menu(wxCommandEvent& e);
|
||||||
|
bool scroll_to_end_on_repaint;
|
||||||
protected:
|
protected:
|
||||||
void prepare_paint();
|
void prepare_paint();
|
||||||
private:
|
private:
|
||||||
|
@ -413,6 +541,8 @@ namespace
|
||||||
int cpuid;
|
int cpuid;
|
||||||
volatile bool trace_active;
|
volatile bool trace_active;
|
||||||
debug_handle trace_handle;
|
debug_handle trace_handle;
|
||||||
|
void do_rwx_break(uint64_t addr, uint64_t value, debug_type type);
|
||||||
|
void kill_debug_hooks();
|
||||||
scroll_bar* scroll;
|
scroll_bar* scroll;
|
||||||
_panel* panel;
|
_panel* panel;
|
||||||
bool broken;
|
bool broken;
|
||||||
|
@ -426,6 +556,9 @@ namespace
|
||||||
uint64_t find_line;
|
uint64_t find_line;
|
||||||
std::string find_string;
|
std::string find_string;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
|
bool singlestepping;
|
||||||
|
std::map<std::pair<uint64_t, debug_type>, debug_handle> rwx_breakpoints;
|
||||||
|
wxMenuItem* m_singlestep;
|
||||||
};
|
};
|
||||||
|
|
||||||
wxwin_tracelog::~wxwin_tracelog()
|
wxwin_tracelog::~wxwin_tracelog()
|
||||||
|
@ -440,13 +573,27 @@ namespace
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(trace_active)
|
if(trace_active)
|
||||||
runemufn([this]() { debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle); });
|
runemufn([this]() { kill_debug_hooks(); });
|
||||||
trace_active = false;
|
trace_active = false;
|
||||||
if(!closing)
|
if(!closing)
|
||||||
Destroy();
|
Destroy();
|
||||||
closing = true;
|
closing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxwin_tracelog::kill_debug_hooks()
|
||||||
|
{
|
||||||
|
debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle);
|
||||||
|
umutex_class h(buffer_mutex);
|
||||||
|
for(auto& i : rwx_breakpoints) {
|
||||||
|
if(!i.second.handle)
|
||||||
|
continue;
|
||||||
|
debug_remove_callback(i.first.first, i.first.second, i.second);
|
||||||
|
//Dirty hack.
|
||||||
|
i.second.handle = NULL;
|
||||||
|
}
|
||||||
|
convert_break_to_pause();
|
||||||
|
}
|
||||||
|
|
||||||
wxwin_tracelog::_panel::_panel(wxwin_tracelog* parent)
|
wxwin_tracelog::_panel::_panel(wxwin_tracelog* parent)
|
||||||
: text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL)
|
: text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL)
|
||||||
{
|
{
|
||||||
|
@ -455,6 +602,7 @@ namespace
|
||||||
pressed_row = 0;
|
pressed_row = 0;
|
||||||
current_row = 0;
|
current_row = 0;
|
||||||
holding = false;
|
holding = false;
|
||||||
|
scroll_to_end_on_repaint = false;
|
||||||
this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_tracelog::_panel::on_size), NULL, this);
|
this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_tracelog::_panel::on_size), NULL, this);
|
||||||
this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
|
this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
|
||||||
this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
|
this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
|
||||||
|
@ -510,6 +658,11 @@ namespace
|
||||||
void wxwin_tracelog::_panel::prepare_paint()
|
void wxwin_tracelog::_panel::prepare_paint()
|
||||||
{
|
{
|
||||||
p->get_scroll()->set_range(rows.size());
|
p->get_scroll()->set_range(rows.size());
|
||||||
|
if(scroll_to_end_on_repaint) {
|
||||||
|
scroll_to_end_on_repaint = false;
|
||||||
|
p->get_scroll()->set_position(rows.size());
|
||||||
|
pos = p->get_scroll()->get_position();
|
||||||
|
}
|
||||||
uint64_t m = min(pressed_row, current_row);
|
uint64_t m = min(pressed_row, current_row);
|
||||||
uint64_t M = max(pressed_row, current_row);
|
uint64_t M = max(pressed_row, current_row);
|
||||||
auto s = get_characters();
|
auto s = get_characters();
|
||||||
|
@ -521,6 +674,8 @@ namespace
|
||||||
uint32_t bg = selected ? 0x000000 : (isfl ? 0xC0FFC0 : 0xFFFFFF);
|
uint32_t bg = selected ? 0x000000 : (isfl ? 0xC0FFC0 : 0xFFFFFF);
|
||||||
write(rows[i], s.first, 0, i - pos, fg, bg);
|
write(rows[i], s.first, 0, i - pos, fg, bg);
|
||||||
}
|
}
|
||||||
|
for(uint64_t i = rows.size(); i < pos + s.second; i++)
|
||||||
|
write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxwin_tracelog::process_lines()
|
void wxwin_tracelog::process_lines()
|
||||||
|
@ -535,9 +690,16 @@ namespace
|
||||||
panel->rows.push_back(i);
|
panel->rows.push_back(i);
|
||||||
lines_waiting.clear();
|
lines_waiting.clear();
|
||||||
unprocessed_lines = false;
|
unprocessed_lines = false;
|
||||||
panel->request_paint();
|
if(panel->rows.size() != osize) {
|
||||||
if(panel->rows.size() != osize)
|
panel->scroll_to_end_on_repaint = true;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
}
|
||||||
|
panel->request_paint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxwin_tracelog::do_rwx_break(uint64_t addr, uint64_t value, debug_type type)
|
||||||
|
{
|
||||||
|
debug_request_break();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxwin_tracelog::on_enabled(wxCommandEvent& e)
|
void wxwin_tracelog::on_enabled(wxCommandEvent& e)
|
||||||
|
@ -545,10 +707,24 @@ namespace
|
||||||
bool enable = enabled->GetValue();
|
bool enable = enabled->GetValue();
|
||||||
runemufn([this, enable]() {
|
runemufn([this, enable]() {
|
||||||
if(enable) {
|
if(enable) {
|
||||||
|
umutex_class h(buffer_mutex);
|
||||||
broken = broken2;
|
broken = broken2;
|
||||||
broken2 = true;
|
broken2 = true;
|
||||||
|
for(auto& i : rwx_breakpoints) {
|
||||||
|
auto i2 = i.first;
|
||||||
|
i.second = debug_add_callback(i.first.first, i.first.second,
|
||||||
|
[this, i2](uint64_t addr, uint64_t value) {
|
||||||
|
this->do_rwx_break(addr, value, i2.second);
|
||||||
|
}, [this, i2] {
|
||||||
|
//We need to kill this hook if still active.
|
||||||
|
auto& h = rwx_breakpoints[i2];
|
||||||
|
if(h.handle)
|
||||||
|
debug_remove_callback(i2.first, i2.second, h);
|
||||||
|
h.handle = NULL;
|
||||||
|
});
|
||||||
|
}
|
||||||
this->trace_handle = debug_add_trace_callback(cpuid, [this](uint64_t proc,
|
this->trace_handle = debug_add_trace_callback(cpuid, [this](uint64_t proc,
|
||||||
const char* str) {
|
const char* str, bool true_instruction) {
|
||||||
if(!this->trace_active)
|
if(!this->trace_active)
|
||||||
return;
|
return;
|
||||||
//Got tracelog line, send it.
|
//Got tracelog line, send it.
|
||||||
|
@ -558,22 +734,31 @@ namespace
|
||||||
this->unprocessed_lines = true;
|
this->unprocessed_lines = true;
|
||||||
runuifun([this]() { this->process_lines(); });
|
runuifun([this]() { this->process_lines(); });
|
||||||
}
|
}
|
||||||
|
if(this->singlestepping && true_instruction) {
|
||||||
|
debug_request_break();
|
||||||
|
this->singlestepping = false;
|
||||||
|
}
|
||||||
}, [this]() {
|
}, [this]() {
|
||||||
//Dtor!
|
//Dtor!
|
||||||
auto tmp = this;
|
auto tmp = this;
|
||||||
if(!tmp->trace_active)
|
if(!tmp->trace_active)
|
||||||
return;
|
return;
|
||||||
this->trace_active = false;
|
this->trace_active = false;
|
||||||
debug_remove_callback(cpuid, DEBUG_TRACE, this->trace_handle);
|
debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle);
|
||||||
|
tmp->kill_debug_hooks();
|
||||||
//We can't use this anymore.
|
//We can't use this anymore.
|
||||||
runuifun([tmp]() { tmp->enabled->SetValue(false); });
|
runuifun([tmp]() {
|
||||||
|
tmp->enabled->SetValue(false);
|
||||||
|
tmp->m_singlestep->Enable(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
this->trace_active = true;
|
this->trace_active = true;
|
||||||
} else if(trace_active) {
|
} else if(trace_active) {
|
||||||
this->trace_active = false;
|
this->trace_active = false;
|
||||||
debug_remove_callback(cpuid, DEBUG_TRACE, this->trace_handle);
|
this->kill_debug_hooks();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
m_singlestep->Enable(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool find_match(const std::string& pattern, const std::string& candidate)
|
bool find_match(const std::string& pattern, const std::string& candidate)
|
||||||
|
@ -635,8 +820,9 @@ namespace
|
||||||
if(r < 0 || (r > 0 && !do_exit_save()))
|
if(r < 0 || (r > 0 && !do_exit_save()))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(trace_active)
|
if(trace_active) {
|
||||||
runemufn([this]() { debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle); });
|
runemufn([this]() { this->kill_debug_hooks(); });
|
||||||
|
}
|
||||||
trace_active = false;
|
trace_active = false;
|
||||||
Destroy();
|
Destroy();
|
||||||
return;
|
return;
|
||||||
|
@ -709,6 +895,22 @@ namespace
|
||||||
find_line--;
|
find_line--;
|
||||||
}
|
}
|
||||||
scroll_pane(find_line);
|
scroll_pane(find_line);
|
||||||
|
} else if(e.GetId() == wxID_SINGLESTEP) {
|
||||||
|
runemufn_async([this]() {
|
||||||
|
this->singlestepping = true;
|
||||||
|
lsnes_cmd.invoke("unpause-emulator");
|
||||||
|
});
|
||||||
|
} else if(e.GetId() == wxID_FRAMEADVANCE) {
|
||||||
|
runemufn_async([this]() {
|
||||||
|
lsnes_cmd.invoke("+advance-frame");
|
||||||
|
lsnes_cmd.invoke("-advance-frame");
|
||||||
|
});
|
||||||
|
} else if(e.GetId() == wxID_CONTINUE) {
|
||||||
|
runemufn_async([this]() { lsnes_cmd.invoke("unpause-emulator"); });
|
||||||
|
} else if(e.GetId() == wxID_BREAKPOINTS) {
|
||||||
|
dialog_breakpoints* d = new dialog_breakpoints(this);
|
||||||
|
d->ShowModal();
|
||||||
|
d->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,7 +963,7 @@ namespace
|
||||||
unsigned offset = r / 2;
|
unsigned offset = r / 2;
|
||||||
if(offset > line)
|
if(offset > line)
|
||||||
scroll->set_position(panel->pos = 0);
|
scroll->set_position(panel->pos = 0);
|
||||||
else if(line + r < panel->rows.size())
|
else if(line + r <= panel->rows.size())
|
||||||
scroll->set_position(panel->pos = line - offset);
|
scroll->set_position(panel->pos = line - offset);
|
||||||
else
|
else
|
||||||
scroll->set_position(panel->pos = panel->rows.size() - r);
|
scroll->set_position(panel->pos = panel->rows.size() - r);
|
||||||
|
@ -790,12 +992,52 @@ back:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<std::pair<uint64_t, debug_type>> wxwin_tracelog::get_breakpoints()
|
||||||
|
{
|
||||||
|
std::set<std::pair<uint64_t, debug_type>> ret;
|
||||||
|
runemufn([this, &ret]() {
|
||||||
|
for(auto i : rwx_breakpoints)
|
||||||
|
ret.insert(i.first);
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxwin_tracelog::add_breakpoint(uint64_t addr, debug_type dtype)
|
||||||
|
{
|
||||||
|
std::pair<uint64_t, debug_type> i2 = std::make_pair(addr, dtype);
|
||||||
|
if(!trace_active) {
|
||||||
|
//We'll register this later.
|
||||||
|
rwx_breakpoints[i2] = debug_handle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rwx_breakpoints[i2] = debug_add_callback(i2.first, i2.second,
|
||||||
|
[this, i2](uint64_t addr, uint64_t value) {
|
||||||
|
this->do_rwx_break(addr, value, i2.second);
|
||||||
|
}, [this, i2] {
|
||||||
|
//We need to kill this hook if still active.
|
||||||
|
auto& h = rwx_breakpoints[i2];
|
||||||
|
if(h.handle)
|
||||||
|
debug_remove_callback(i2.first, i2.second, h);
|
||||||
|
h.handle = NULL;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxwin_tracelog::remove_breakpoint(uint64_t addr, debug_type dtype)
|
||||||
|
{
|
||||||
|
std::pair<uint64_t, debug_type> i2 = std::make_pair(addr, dtype);
|
||||||
|
auto& h = rwx_breakpoints[i2];
|
||||||
|
if(h.handle)
|
||||||
|
debug_remove_callback(i2.first, i2.second, h);
|
||||||
|
rwx_breakpoints.erase(i2);
|
||||||
|
}
|
||||||
|
|
||||||
wxwin_tracelog::wxwin_tracelog(wxWindow* parent, int _cpuid, const std::string& cpuname)
|
wxwin_tracelog::wxwin_tracelog(wxWindow* parent, int _cpuid, const std::string& cpuname)
|
||||||
: wxFrame(parent, wxID_ANY, towxstring("lsnes: Tracelog for " + cpuname), wxDefaultPosition,
|
: wxFrame(parent, wxID_ANY, towxstring("lsnes: Tracelog for " + cpuname), wxDefaultPosition,
|
||||||
wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
|
wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
|
||||||
wxCLIP_CHILDREN)
|
wxCLIP_CHILDREN)
|
||||||
{
|
{
|
||||||
cpuid = _cpuid;
|
cpuid = _cpuid;
|
||||||
|
singlestepping = false;
|
||||||
find_active = false;
|
find_active = false;
|
||||||
find_line = 0;
|
find_line = 0;
|
||||||
closing = false;
|
closing = false;
|
||||||
|
@ -834,6 +1076,13 @@ back:
|
||||||
menu->Append(wxID_FIND, wxT("Find..."));
|
menu->Append(wxID_FIND, wxT("Find..."));
|
||||||
menu->Append(wxID_FIND_NEXT, wxT("Find next\tF3"));
|
menu->Append(wxID_FIND_NEXT, wxT("Find next\tF3"));
|
||||||
menu->Append(wxID_FIND_PREV, wxT("Find previous\tSHIFT+F3"));
|
menu->Append(wxID_FIND_PREV, wxT("Find previous\tSHIFT+F3"));
|
||||||
|
mb->Append(menu = new wxMenu(), wxT("Debug"));
|
||||||
|
m_singlestep = menu->Append(wxID_SINGLESTEP, towxstring("Singlestep\tF2"));
|
||||||
|
menu->Append(wxID_FRAMEADVANCE, towxstring("Frame advance\tF4"));
|
||||||
|
menu->Append(wxID_CONTINUE, towxstring("Continue\tF5"));
|
||||||
|
menu->AppendSeparator();
|
||||||
|
menu->Append(wxID_BREAKPOINTS, towxstring("Breakpoints"));
|
||||||
|
m_singlestep->Enable(false);
|
||||||
Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_tracelog::on_menu),
|
Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_tracelog::on_menu),
|
||||||
NULL, this);
|
NULL, this);
|
||||||
//Very nasty hack.
|
//Very nasty hack.
|
||||||
|
@ -1185,23 +1434,27 @@ back:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string format_vma_offset(memory_region& region, uint64_t offset)
|
||||||
|
{
|
||||||
|
std::ostringstream y;
|
||||||
|
y << region.name;
|
||||||
|
size_t sizedigits = 0;
|
||||||
|
uint64_t tmp = region.size - 1;
|
||||||
|
while(tmp > 0) {
|
||||||
|
tmp >>= 4;
|
||||||
|
sizedigits++;
|
||||||
|
}
|
||||||
|
y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << offset;
|
||||||
|
return y.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string lookup_address(uint64_t raw)
|
std::string lookup_address(uint64_t raw)
|
||||||
{
|
{
|
||||||
auto g = lsnes_memory.lookup(raw);
|
auto g = lsnes_memory.lookup(raw);
|
||||||
if(!g.first)
|
if(!g.first)
|
||||||
return hex::to<uint64_t>(raw);
|
return hex::to<uint64_t>(raw);
|
||||||
else {
|
else
|
||||||
std::ostringstream y;
|
return format_vma_offset(*g.first, g.second);
|
||||||
y << g.first->name;
|
|
||||||
size_t sizedigits = 0;
|
|
||||||
uint64_t tmp = g.first->size - 1;
|
|
||||||
while(tmp > 0) {
|
|
||||||
tmp >>= 4;
|
|
||||||
sizedigits++;
|
|
||||||
}
|
|
||||||
y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << g.second;
|
|
||||||
return y.str();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int sign_compare(uint64_t a, uint64_t b)
|
inline int sign_compare(uint64_t a, uint64_t b)
|
||||||
|
@ -1456,6 +1709,112 @@ back:
|
||||||
top_s->SetSizeHints(this);
|
top_s->SetSizeHints(this);
|
||||||
SetClientSize(tmp2);
|
SetClientSize(tmp2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog_breakpoints::dialog_breakpoints(wxwin_tracelog* parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, "Breakpoints")
|
||||||
|
{
|
||||||
|
pwin = parent;
|
||||||
|
regions = lsnes_memory.get_regions();
|
||||||
|
wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
|
||||||
|
SetSizer(top_s);
|
||||||
|
top_s->Add(brklist = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400)), 1, wxGROW);
|
||||||
|
brklist->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
|
||||||
|
wxCommandEventHandler(dialog_breakpoints::on_selchange), NULL, this);
|
||||||
|
populate_breakpoints();
|
||||||
|
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
pbutton_s->Add(addb = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
|
||||||
|
pbutton_s->Add(delb = new wxButton(this, wxID_ANY, wxT("Remove")), 0, wxGROW);
|
||||||
|
pbutton_s->AddStretchSpacer();
|
||||||
|
pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
|
||||||
|
delb->Enable(false);
|
||||||
|
addb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_add), NULL,
|
||||||
|
this);
|
||||||
|
delb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_delete),
|
||||||
|
NULL, this);
|
||||||
|
ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_ok), NULL,
|
||||||
|
this);
|
||||||
|
top_s->Add(pbutton_s, 0, wxGROW);
|
||||||
|
top_s->SetSizeHints(this);
|
||||||
|
Fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_breakpoints::populate_breakpoints()
|
||||||
|
{
|
||||||
|
auto t = pwin->get_breakpoints();
|
||||||
|
for(auto i : t) {
|
||||||
|
std::string line = format_line(i);
|
||||||
|
unsigned insert_pos = get_insert_pos(i);
|
||||||
|
brklist->Insert(towxstring(line), insert_pos);
|
||||||
|
listsyms.insert(listsyms.begin() + insert_pos, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_breakpoints::on_add(wxCommandEvent& e)
|
||||||
|
{
|
||||||
|
uint64_t addr;
|
||||||
|
debug_type dtype;
|
||||||
|
dialog_breakpoint_add* d = new dialog_breakpoint_add(this, regions);
|
||||||
|
if(d->ShowModal() != wxID_OK) {
|
||||||
|
d->Destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rpair(addr, dtype) = d->get_result();
|
||||||
|
d->Destroy();
|
||||||
|
runemufn_async([this, addr, dtype]() { pwin->add_breakpoint(addr, dtype); });
|
||||||
|
auto ent = std::make_pair(addr, dtype);
|
||||||
|
std::string line = format_line(ent);
|
||||||
|
unsigned insert_pos = get_insert_pos(ent);
|
||||||
|
brklist->Insert(towxstring(line), insert_pos);
|
||||||
|
listsyms.insert(listsyms.begin() + insert_pos, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_breakpoints::on_delete(wxCommandEvent& e)
|
||||||
|
{
|
||||||
|
int idx = brklist->GetSelection();
|
||||||
|
if(idx == wxNOT_FOUND)
|
||||||
|
return;
|
||||||
|
uint64_t addr;
|
||||||
|
debug_type dtype;
|
||||||
|
addr = listsyms[idx].first;
|
||||||
|
dtype = listsyms[idx].second;
|
||||||
|
runemufn_async([this, addr, dtype]() { pwin->remove_breakpoint(addr, dtype); });
|
||||||
|
brklist->Delete(idx);
|
||||||
|
listsyms.erase(listsyms.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dialog_breakpoints::get_insert_pos(std::pair<uint64_t, debug_type> entry)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
for(i = 0; i < listsyms.size(); i++)
|
||||||
|
if(entry < listsyms[i])
|
||||||
|
return i;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dialog_breakpoints::format_line(std::pair<uint64_t, debug_type> entry)
|
||||||
|
{
|
||||||
|
std::string base = "";
|
||||||
|
for(auto i : regions) {
|
||||||
|
if(entry.first >= i->base && entry.first < i->base + i->size) {
|
||||||
|
base = format_vma_offset(*i, entry.first - i->base);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(base == "")
|
||||||
|
base = hex::to<uint64_t>(entry.first);
|
||||||
|
if(entry.second == DEBUG_READ)
|
||||||
|
return base + ": Read";
|
||||||
|
if(entry.second == DEBUG_WRITE)
|
||||||
|
return base + ": Write";
|
||||||
|
if(entry.second == DEBUG_EXEC)
|
||||||
|
return base + ": Execute";
|
||||||
|
return base + ": Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_breakpoints::on_selchange(wxCommandEvent& e)
|
||||||
|
{
|
||||||
|
delb->Enable(brklist->GetSelection() != wxNOT_FOUND);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxeditor_tracelog_display(wxWindow* parent, int cpuid, const std::string& cpuname)
|
void wxeditor_tracelog_display(wxWindow* parent, int cpuid, const std::string& cpuname)
|
||||||
|
|
Loading…
Add table
Reference in a new issue