From 81079946a4e1308ec4c02f69e9aebc27ec4bb3d1 Mon Sep 17 00:00:00 2001 From: cracyc Date: Sun, 23 Jul 2017 20:41:08 -0500 Subject: luaengine: debugger_manager support [Carl] --- 3rdparty/linenoise/linenoise.c | 34 +++++++++- 3rdparty/linenoise/linenoise.h | 2 + 3rdparty/lua-linenoise/linenoise.c | 7 ++ plugins/console/init.lua | 20 ++++++ src/frontend/mame/luaengine.cpp | 133 ++++++++++++++++++++++++++++++++++++- 5 files changed, 192 insertions(+), 4 deletions(-) diff --git a/3rdparty/linenoise/linenoise.c b/3rdparty/linenoise/linenoise.c index 1291d08d9ed..3c63ffe4a96 100644 --- a/3rdparty/linenoise/linenoise.c +++ b/3rdparty/linenoise/linenoise.c @@ -160,6 +160,7 @@ static int history_len = 0; static char **history = NULL; static char preload_buf[LINENOISE_MAX_LINE]; static int preload = 0; +static int refresh = 0; /* Structure to contain the status of the current (being edited) line */ struct current { @@ -184,6 +185,7 @@ struct current { static int fd_read(struct current *current); static int getWindowSize(struct current *current); +static void refreshLine(const char *prompt, struct current *current); void linenoiseHistoryFree(void) { if (history) { @@ -408,6 +410,23 @@ static int fd_read_char(int fd, int timeout) */ static int fd_read(struct current *current) { + struct pollfd p; + int ret; + p.fd = current->fd; + p.events = POLLIN; + while(1) { + ret = poll(&p, 1, 100); + if (ret == -1) + return -1; + else if (!ret) { + if (!refresh) + continue; + refresh = 0; + refreshLine(current->prompt, current); + } + else + break; + } #ifdef USE_UTF8 char buf[4]; int n; @@ -799,7 +818,15 @@ static int fd_read(struct current *current) while (1) { INPUT_RECORD irec; DWORD n; - if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) { + DWORD ret = WaitForSingleObject(current->inh, 100); + if (ret == WAIT_TIMEOUT) { + if (!refresh) + continue; + refresh = 0; + refreshLine(current->prompt, current); + continue; + } + else if (ret != WAIT_OBJECT_0) { break; } if (!ReadConsoleInput (current->inh, &irec, 1, &n)) { @@ -1580,6 +1607,11 @@ void linenoisePreloadBuffer(const char* preloadText) strcpy(preload_buf, preloadText); } +void linenoiseRefresh(void) +{ + refresh = 1; +} + char *linenoise(const char *prompt) { int count; diff --git a/3rdparty/linenoise/linenoise.h b/3rdparty/linenoise/linenoise.h index d6688099c42..aa1f77ec819 100644 --- a/3rdparty/linenoise/linenoise.h +++ b/3rdparty/linenoise/linenoise.h @@ -125,4 +125,6 @@ int linenoiseColumns(void); void linenoisePreloadBuffer(const char* preloadText); +void linenoiseRefresh(void); + #endif /* __LINENOISE_H */ diff --git a/3rdparty/lua-linenoise/linenoise.c b/3rdparty/lua-linenoise/linenoise.c index f7a8a60d1de..768d455729d 100644 --- a/3rdparty/lua-linenoise/linenoise.c +++ b/3rdparty/lua-linenoise/linenoise.c @@ -169,6 +169,12 @@ static int l_addcompletion(lua_State *L) return handle_ln_ok(L); } +static int l_refresh(lua_State *L) +{ + linenoiseRefresh(); + return handle_ln_ok(L); +} + luaL_Reg linenoise_funcs[] = { { "linenoise", l_linenoise }, { "historyadd", l_historyadd }, @@ -179,6 +185,7 @@ luaL_Reg linenoise_funcs[] = { { "setcompletion", l_setcompletion}, { "addcompletion", l_addcompletion }, { "preload", l_preloadbuffer }, + { "refresh", l_refresh }, /* Aliases for more consistent function names */ { "addhistory", l_historyadd }, diff --git a/plugins/console/init.lua b/plugins/console/init.lua index ba3ba7d6bd4..0c503adbaa5 100644 --- a/plugins/console/init.lua +++ b/plugins/console/init.lua @@ -16,6 +16,8 @@ function console.startplugin() local ln = require("linenoise") local preload = false local matches = {} + local lastindex = 0 + local consolebuf print(" _/ _/ _/_/ _/ _/ _/_/_/_/"); print(" _/_/ _/_/ _/ _/ _/_/ _/_/ _/ "); print(" _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/ "); @@ -197,7 +199,25 @@ return ln.linenoise('\x1b[1;36m[MAME]\x1b[0m> ') return "\x01" .. "-1" end + emu.register_start(function() + if not consolebuf and manager:machine():debugger() then + consolebuf = manager:machine():debugger().consolelog + lastindex = 0 + end + end) + + emu.register_stop(function() consolebuf = nil end) + emu.register_periodic(function() + if consolebuf and (#consolebuf > lastindex) then + local last = #consolebuf + print("\n") + while lastindex < last do + lastindex = lastindex + 1 + print(consolebuf[lastindex]) + end + ln.refresh() + end if conth.yield then conth:continue(get_completions(conth.result:match("([^\x01]*)\x01(.*)"))) return diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index a7bfd10114e..518aa0da83a 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -12,6 +12,10 @@ #include #include "emu.h" #include "mame.h" +#include "debugger.h" +#include "debug/debugcon.h" +#include "debug/debugcpu.h" +#include "debug/textbuf.h" #include "drivenum.h" #include "emuopts.h" #include "ui/ui.h" @@ -1133,9 +1137,9 @@ void lua_engine::initialize() * machine:input() - get input_manager * machine:uiinput() - get ui_input_manager * machine.paused - get paused state - * machine.devices - get device table - * machine.screens - get screens table - * machine.images - get available image devices + * machine.devices[] - get device table + * machine.screens[] - get screens table + * machine.images[] - get available image devices * machine:popmessage(str) - print str as popup * machine:logerror(str) - print str to log */ @@ -1156,6 +1160,11 @@ void lua_engine::initialize() "outputs", &running_machine::output, "input", &running_machine::input, "uiinput", &running_machine::ui_input, + "debugger", [this](running_machine &m) -> sol::object { + if(!(m.debug_flags & DEBUG_FLAG_ENABLED)) + return sol::make_object(sol(), sol::nil); + return sol::make_object(sol(), &m.debugger()); + }, "paused", sol::property(&running_machine::paused), "devices", sol::property([this](running_machine &m) { std::function tree; @@ -1209,11 +1218,124 @@ void lua_engine::initialize() "compatible_with", sol::readonly(&game_driver::compatible_with), "default_layout", sol::readonly(&game_driver::default_layout)); +/* machine:debugger() + * debug:command(cmd) - run cmd in debugger console + * debug.consolelog[] - get consolelog + * debug.errorlog[] - get errorlog + * debug.visible_cpu - accessor for debugger active cpu for commands, affects debug views + * debug.execution_state - accessor for active cpu run state + */ + + struct wrap_textbuf { wrap_textbuf(text_buffer *buf) { textbuf = buf; }; text_buffer *textbuf; }; + + sol().registry().new_usertype("debugger", "new", sol::no_constructor, + "command", [](debugger_manager &debug, const std::string &cmd) { debug.console().execute_command(cmd, false); }, + "consolelog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); }), + "errorlog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); }), + "visible_cpu", sol::property([](debugger_manager &debug) { debug.cpu().get_visible_cpu(); }, + [](debugger_manager &debug, device_t &dev) { debug.cpu().set_visible_cpu(&dev); }), + "execution_state", sol::property([](debugger_manager &debug) { + int execstate = debug.cpu().execution_state(); + if(execstate == 0) + return "stop"; + return "running"; + }, + [](debugger_manager &debug, const std::string &state) { + int execstate = 1; + if(state == "stop") + execstate = 0; + debug.cpu().set_execution_state(execstate); + })); + + sol().registry().new_usertype("text_buffer", "new", sol::no_constructor, + "__metatable", [](){}, + "__newindex", [](){}, + "__index", [](wrap_textbuf &buf, int index) { return text_buffer_get_seqnum_line(buf.textbuf, index - 1); }, + "__len", [](wrap_textbuf &buf) { return text_buffer_num_lines(buf.textbuf) + text_buffer_line_index_to_seqnum(buf.textbuf, 0) - 1; }); + +/* device:debug() + * debug:step([opt] steps) - run cpu steps, default 1 + * debug:go() - run cpu + * debug:bpset(addr, cond, act) - set break, cond and act are debugger expressions + * debug:bpclr(idx) - clear break + * debug:bplist()[] - table of breakpoints + * debug:wpset(space, type, addr, len, cond, act) - set watch, cond and act are debugger expressions + * debug:wpclr(idx) - clear watch + * debug:wplist(space)[] - table of watchpoints + */ + sol().registry().new_usertype("device_debug", "new", sol::no_constructor, + "step", [this](device_debug &dev, sol::object num) { + int steps = 1; + if(num.is()) + steps = num.as(); + dev.single_step(steps); + }, + "go", &device_debug::go, + "bpset", [](device_debug &dev, offs_t addr, const char *cond, const char *act) { return dev.breakpoint_set(addr, cond, act); }, + "bpclr", &device_debug::breakpoint_clear, + "bplist", [this](device_debug &dev) { + sol::table table = sol().create_table(); + device_debug::breakpoint *list = dev.breakpoint_first(); + while(list) + { + sol::table bp = sol().create_table(); + bp["enabled"] = list->enabled(); + bp["address"] = list->address(); + bp["condition"] = list->condition(); + bp["action"] = list->action(); + table[list->index()] = bp; + list = list->next(); + } + return table; + }, + "wpset", [](device_debug &dev, addr_space &sp, const std::string &type, offs_t addr, offs_t len, const char *cond, const char *act) { + int wptype = WATCHPOINT_READ; + if(type == "w") + wptype = WATCHPOINT_WRITE; + else if((type == "rw") || (type == "wr")) + wptype = WATCHPOINT_READ | WATCHPOINT_WRITE; + return dev.watchpoint_set(sp.space, wptype, addr, len, cond, act); + }, + "wpclr", &device_debug::watchpoint_clear, + "wplist", [this](device_debug &dev, addr_space &sp) { + sol::table table = sol().create_table(); + device_debug::watchpoint *list = dev.watchpoint_first(sp.space.spacenum()); + while(list) + { + sol::table wp = sol().create_table(); + wp["enabled"] = list->enabled(); + wp["address"] = list->address(); + wp["length"] = list->length(); + switch(list->type()) + { + case WATCHPOINT_READ: + wp["type"] = "r"; + break; + case WATCHPOINT_WRITE: + wp["type"] = "w"; + break; + case WATCHPOINT_READ | WATCHPOINT_WRITE: + wp["type"] = "rw"; + break; + default: // huh? + wp["type"] = ""; + break; + } + wp["condition"] = list->condition(); + wp["action"] = list->action(); + table[list->index()] = wp; + list = list->next(); + } + return table; + }); + + /* machine.devices[device_tag] * device:name() - device long name * device:shortname() - device short name * device:tag() - device tree tag * device:owner() - device parent tag + * device:debug() - debug interface, cpus only * device.spaces[] - device address spaces table * device.state[] - device state entries table * device.items[] - device save state items table @@ -1224,6 +1346,11 @@ void lua_engine::initialize() "shortname", &device_t::shortname, "tag", &device_t::tag, "owner", &device_t::owner, + "debug", [this](device_t &dev) -> sol::object { + if(!(dev.machine().debug_flags & DEBUG_FLAG_ENABLED) || !dynamic_cast(&dev)) // debugger not enabled or not cpu + return sol::make_object(sol(), sol::nil); + return sol::make_object(sol(), dev.debug()); + }, "spaces", sol::property([this](device_t &dev) { device_memory_interface *memdev = dynamic_cast(&dev); sol::table sp_table = sol().create_table(); -- cgit v1.2.3