summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/luaengine_debug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/luaengine_debug.cpp')
-rw-r--r--src/frontend/mame/luaengine_debug.cpp542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/frontend/mame/luaengine_debug.cpp b/src/frontend/mame/luaengine_debug.cpp
new file mode 100644
index 00000000000..8cdf29246c0
--- /dev/null
+++ b/src/frontend/mame/luaengine_debug.cpp
@@ -0,0 +1,542 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic,Luca Bruno
+/***************************************************************************
+
+ luaengine.cpp
+
+ Controls execution of the core MAME system.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "luaengine.ipp"
+
+#include "debug/debugcon.h"
+#include "debug/debugcpu.h"
+#include "debug/debugvw.h"
+#include "debug/express.h"
+#include "debug/points.h"
+#include "debug/textbuf.h"
+#include "debugger.h"
+
+
+namespace {
+
+struct wrap_textbuf
+{
+ wrap_textbuf(text_buffer const &buf) : textbuf(buf) { }
+
+ std::reference_wrapper<const text_buffer> textbuf;
+};
+
+
+template <bool Enable>
+sol::object do_breakpoint_enable(device_debug &dev, sol::this_state s, sol::object index)
+{
+ if (index == sol::lua_nil)
+ {
+ dev.breakpoint_enable_all(Enable);
+ dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ return sol::lua_nil;
+ }
+ else if (index.is<int>())
+ {
+ bool result(dev.breakpoint_enable(index.as<int>(), Enable));
+ if (result)
+ {
+ dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ }
+ return sol::make_object(s, result);
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call bpenable with integer or nil\n");
+ return sol::lua_nil;
+ }
+}
+
+
+template <bool Enable>
+sol::object do_watchpoint_enable(device_debug &dev, sol::this_state s, sol::object index)
+{
+ if (index == sol::lua_nil)
+ {
+ dev.watchpoint_enable_all(Enable);
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return sol::lua_nil;
+ }
+ else if (index.is<int>())
+ {
+ bool result(dev.watchpoint_enable(index.as<int>(), Enable));
+ if (result)
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return sol::make_object(s, result);
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call wpenable with integer or nil");
+ return sol::lua_nil;
+ }
+}
+
+} // anonymous namespace
+
+
+class lua_engine::symbol_table_wrapper
+{
+public:
+ symbol_table_wrapper(symbol_table_wrapper const &) = delete;
+
+ symbol_table_wrapper(lua_engine &host, running_machine &machine, std::shared_ptr<symbol_table_wrapper> const &parent, device_t *device)
+ : m_host(host)
+ , m_table(machine, parent ? &parent->table() : nullptr, device)
+ , m_parent(parent)
+ {
+ }
+
+ symbol_table &table() { return m_table; }
+ symbol_table const &table() const { return m_table; }
+ std::shared_ptr<symbol_table_wrapper> const &parent() { return m_parent; }
+
+ symbol_entry &add(char const *name) { return m_table.add(name, symbol_table::READ_WRITE); }
+ symbol_entry &add(char const *name, u64 value) { return m_table.add(name, value); }
+ symbol_entry &add(char const *name, sol::protected_function getter, std::optional<sol::protected_function> setter, std::optional<char const *> format)
+ {
+ symbol_table::setter_func setfun;
+ if (setter)
+ {
+ setfun =
+ [this, cbfunc = sol::protected_function(m_host.m_lua_state, *setter)] (u64 value)
+ {
+ auto status = m_host.invoke(cbfunc, value);
+ if (!status.valid())
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA EROR] in symbol value setter callback: %s\n", err.what());
+ }
+ };
+ }
+ return m_table.add(
+ name,
+ [this, cbfunc = sol::protected_function(m_host.m_lua_state, getter)] () -> u64
+ {
+ auto status = m_host.invoke(cbfunc);
+ if (status.valid())
+ {
+ auto result = status.get<std::optional<u64> >();
+ if (result)
+ return *result;
+
+ osd_printf_error("[LUA EROR] invalid return from symbol value getter callback\n");
+ }
+ else
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA EROR] in symbol value getter callback: %s\n", err.what());
+ }
+ return 0;
+ },
+ std::move(setfun),
+ (format && *format) ? *format : "");
+ }
+ symbol_entry *find(char const *name) const { return m_table.find(name); }
+ symbol_entry *find_deep(char const *name) { return m_table.find_deep(name); }
+
+ u64 value(const char *symbol) { return m_table.value(symbol); }
+ void set_value(const char *symbol, u64 value) { m_table.set_value(symbol, value); }
+
+ u64 read_memory(addr_space &space, offs_t address, int size, bool translate) { return m_table.read_memory(space.space, address, size, translate); }
+ void write_memory(addr_space &space, offs_t address, u64 data, int size, bool translate) { m_table.write_memory(space.space, address, data, size, translate); }
+
+private:
+ lua_engine &m_host;
+ symbol_table m_table;
+ std::shared_ptr<symbol_table_wrapper> const m_parent;
+};
+
+
+class lua_engine::expression_wrapper
+{
+public:
+ expression_wrapper(expression_wrapper const &) = delete;
+ expression_wrapper &operator=(expression_wrapper const &) = delete;
+
+ expression_wrapper(std::shared_ptr<symbol_table_wrapper> const &symtable)
+ : m_expression(symtable->table())
+ , m_symbols(symtable)
+ {
+ }
+
+ void set_default_base(int base) { m_expression.set_default_base(base); }
+
+ void parse(sol::this_state s, char const *string)
+ {
+ try
+ {
+ m_expression.parse(string);
+ }
+ catch (expression_error const &err)
+ {
+ sol::stack::push(s, err);
+ lua_error(s);
+ }
+ }
+
+ u64 execute(sol::this_state s)
+ {
+ try
+ {
+ return m_expression.execute();
+ }
+ catch (expression_error const &err)
+ {
+ sol::stack::push(s, err);
+ lua_error(s);
+ return 0; // unreachable - lua_error doesn't return
+ }
+ }
+
+ bool is_empty() const { return m_expression.is_empty(); }
+ char const *original_string() const { return m_expression.original_string(); }
+ std::shared_ptr<symbol_table_wrapper> const &symbols() { return m_symbols; }
+
+ void set_symbols(std::shared_ptr<symbol_table_wrapper> const &symtable)
+ {
+ m_expression.set_symbols(symtable->table());
+ m_symbols = symtable;
+ }
+
+private:
+ parsed_expression m_expression;
+ std::shared_ptr<symbol_table_wrapper> m_symbols;
+};
+
+
+void lua_engine::initialize_debug(sol::table &emu)
+{
+
+ static const enum_parser<read_or_write, 4> s_read_or_write_parser =
+ {
+ { "r", read_or_write::READ },
+ { "w", read_or_write::WRITE },
+ { "rw", read_or_write::READWRITE },
+ { "wr", read_or_write::READWRITE }
+ };
+
+ static const enum_parser<expression_space, 15> s_expression_space_parser =
+ {
+ { "p", EXPSPACE_PROGRAM_LOGICAL }, { "lp", EXPSPACE_PROGRAM_LOGICAL }, { "pp", EXPSPACE_PROGRAM_PHYSICAL },
+ { "d", EXPSPACE_DATA_LOGICAL }, { "ld", EXPSPACE_DATA_LOGICAL }, { "pd", EXPSPACE_DATA_PHYSICAL },
+ { "i", EXPSPACE_IO_LOGICAL }, { "li", EXPSPACE_IO_LOGICAL }, { "pi", EXPSPACE_IO_PHYSICAL },
+ { "3", EXPSPACE_OPCODE_LOGICAL }, { "l3", EXPSPACE_OPCODE_LOGICAL }, { "p3", EXPSPACE_OPCODE_PHYSICAL },
+ { "r", EXPSPACE_PRGDIRECT },
+ { "o", EXPSPACE_OPDIRECT },
+ { "m", EXPSPACE_REGION }
+ };
+
+ auto expression_error_type = emu.new_usertype<expression_error>(
+ "expression_error",
+ sol::no_constructor);
+ expression_error_type["code"] = sol::property(
+ [] (expression_error const &err) { return int(err.code()); });
+ expression_error_type["offset"] = sol::property(&expression_error::offset);
+ expression_error_type[sol::meta_function::to_string] = &expression_error::code_string;
+
+ auto symbol_table_type = emu.new_usertype<symbol_table_wrapper>(
+ "symbol_table",
+ sol::call_constructor, sol::factories(
+ [this] (running_machine &machine)
+ { return std::make_shared<symbol_table_wrapper>(*this, machine, nullptr, nullptr); },
+ [this] (std::shared_ptr<symbol_table_wrapper> const &parent, device_t *device)
+ { return std::make_shared<symbol_table_wrapper>(*this, parent->table().machine(), parent, device); },
+ [this] (std::shared_ptr<symbol_table_wrapper> const &parent)
+ { return std::make_shared<symbol_table_wrapper>(*this, parent->table().machine(), parent, nullptr); },
+ [this] (device_t &device)
+ { return std::make_shared<symbol_table_wrapper>(*this, device.machine(), nullptr, &device); }));
+ symbol_table_type.set_function("set_memory_modified_func",
+ [this] (symbol_table_wrapper &st, sol::object cb)
+ {
+ if (cb == sol::lua_nil)
+ st.table().set_memory_modified_func(nullptr);
+ else if (cb.is<sol::protected_function>())
+ st.table().set_memory_modified_func([this, cbfunc = sol::protected_function(m_lua_state, cb)] () { invoke(cbfunc); });
+ else
+ osd_printf_error("[LUA ERROR] must call set_memory_modified_func with function or nil\n");
+ });
+ symbol_table_type["add"] = sol::overload(
+ static_cast<symbol_entry &(symbol_table_wrapper::*)(char const *)>(&symbol_table_wrapper::add),
+ static_cast<symbol_entry &(symbol_table_wrapper::*)(char const *, u64)>(&symbol_table_wrapper::add),
+ static_cast<symbol_entry &(symbol_table_wrapper::*)(char const *, sol::protected_function, std::optional<sol::protected_function>, std::optional<char const *>)>(&symbol_table_wrapper::add),
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, sol::lua_nil_t, char const *format) -> symbol_entry &
+ {
+ return st.add(name, getter, std::nullopt, format);
+ },
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, std::optional<sol::protected_function> setter) -> symbol_entry &
+ {
+ return st.add(name, getter, setter, nullptr);
+ },
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, char const *format) -> symbol_entry &
+ {
+ return st.add(name, getter, std::nullopt, format);
+ },
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter) -> symbol_entry &
+ {
+ return st.add(name, getter, std::nullopt, nullptr);
+ },
+ [this] (symbol_table_wrapper &st, char const *name, int minparams, int maxparams, sol::protected_function execute) -> symbol_entry &
+ {
+ return st.table().add(
+ name,
+ minparams,
+ maxparams,
+ [this, cb = sol::protected_function(m_lua_state, execute)] (int numparams, u64 const *paramlist) -> u64
+ {
+ // TODO: C++20 will make this obsolete
+ class helper
+ {
+ private:
+ u64 const *b, *e;
+ public:
+ helper(int n, u64 const *p) : b(p), e(p + n) { }
+ auto begin() const { return b; }
+ auto end() const { return e; }
+ };
+
+ auto status(invoke(cb, sol::as_args(helper(numparams, paramlist))));
+ if (status.valid())
+ {
+ auto result = status.get<std::optional<u64> >();
+ if (result)
+ return *result;
+
+ osd_printf_error("[LUA EROR] invalid return from symbol execute callback\n");
+ }
+ else
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA EROR] in symbol execute callback: %s\n", err.what());
+ }
+ return 0;
+ });
+ });
+ symbol_table_type.set_function("find", &symbol_table_wrapper::find);
+ symbol_table_type.set_function("find_deep", &symbol_table_wrapper::find_deep);
+ symbol_table_type.set_function("value", &symbol_table_wrapper::value);
+ symbol_table_type.set_function("set_value", &symbol_table_wrapper::set_value);
+ symbol_table_type.set_function("memory_value",
+ [] (symbol_table_wrapper &st, char const *name, char const *space, u32 offset, int size, bool disable_se)
+ {
+ expression_space const es = s_expression_space_parser(space);
+ return st.table().memory_value(name, es, offset, size, disable_se);
+ });
+ symbol_table_type.set_function("set_memory_value",
+ [] (symbol_table_wrapper &st, char const *name, char const *space, u32 offset, int size, u64 value, bool disable_se)
+ {
+ expression_space const es = s_expression_space_parser(space);
+ st.table().set_memory_value(name, es, offset, size, value, disable_se);
+ });
+ symbol_table_type.set_function("read_memory", &symbol_table_wrapper::read_memory);
+ symbol_table_type.set_function("write_memory", &symbol_table_wrapper::write_memory);
+ symbol_table_type["entries"] = sol::property([] (symbol_table_wrapper const &st) { return standard_tag_object_ptr_map<symbol_entry>(st.table().entries()); });
+ symbol_table_type["parent"] = sol::property(&symbol_table_wrapper::parent);
+
+
+ auto parsed_expression_type = emu.new_usertype<expression_wrapper>(
+ "parsed_expression",
+ sol::call_constructor, sol::initializers(
+ [] (expression_wrapper &wrapper, std::shared_ptr<symbol_table_wrapper> const &symbols)
+ {
+ new (&wrapper) expression_wrapper(symbols);
+ },
+ [] (expression_wrapper &wrapper, sol::this_state s, std::shared_ptr<symbol_table_wrapper> const &symbols, char const *expression, int base)
+ {
+ new (&wrapper) expression_wrapper(symbols);
+ wrapper.set_default_base(base);
+ wrapper.parse(s, expression);
+ },
+ [] (expression_wrapper &wrapper, sol::this_state s, std::shared_ptr<symbol_table_wrapper> const &symbols, char const *expression)
+ {
+ new (&wrapper) expression_wrapper(symbols);
+ wrapper.parse(s, expression);
+ }));
+ parsed_expression_type.set_function("set_default_base", &expression_wrapper::set_default_base);
+ parsed_expression_type.set_function("parse", &expression_wrapper::parse);
+ parsed_expression_type.set_function("execute", &expression_wrapper::execute);
+ parsed_expression_type["is_empty"] = sol::property(&expression_wrapper::is_empty);
+ parsed_expression_type["original_string"] = sol::property(&expression_wrapper::original_string);
+ parsed_expression_type["symbols"] = sol::property(&expression_wrapper::symbols, &expression_wrapper::set_symbols);
+
+
+ auto symbol_entry_type = sol().registry().new_usertype<symbol_entry>("symbol_entry", sol::no_constructor);
+ symbol_entry_type["name"] = sol::property(&symbol_entry::name);
+ symbol_entry_type["format"] = sol::property(&symbol_entry::format);
+ symbol_entry_type["is_function"] = sol::property(&symbol_entry::is_function);
+ symbol_entry_type["is_lval"] = sol::property(&symbol_entry::is_lval);
+ symbol_entry_type["value"] = sol::property(&symbol_entry::value, &symbol_entry::set_value);
+
+
+ auto debugger_type = sol().registry().new_usertype<debugger_manager>("debugger", sol::no_constructor);
+ debugger_type.set_function("command", [] (debugger_manager &debug, std::string const &cmd) { debug.console().execute_command(cmd, false); });
+ debugger_type["consolelog"] = sol::property([] (debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); });
+ debugger_type["errorlog"] = sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); });
+ debugger_type["visible_cpu"] = sol::property(
+ [](debugger_manager &debug) { return debug.console().get_visible_cpu(); },
+ [](debugger_manager &debug, device_t &dev) { debug.console().set_visible_cpu(&dev); });
+ debugger_type["execution_state"] = sol::property(
+ [] (debugger_manager &debug) { return debug.cpu().is_stopped() ? "stop" : "run"; },
+ [] (debugger_manager &debug, std::string const &state)
+ {
+ if (state == "stop")
+ debug.cpu().set_execution_stopped();
+ else
+ debug.cpu().set_execution_running();
+ });
+
+
+/* wrap_textbuf library (requires debugger to be active)
+ *
+ * manager:machine():debugger().consolelog
+ * manager:machine():debugger().errorlog
+ *
+ * log[index] - get log entry
+ * #log - entry count
+ */
+
+ sol().registry().new_usertype<wrap_textbuf>("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; });
+
+
+ auto device_debug_type = sol().registry().new_usertype<device_debug>("device_debug", sol::no_constructor);
+ device_debug_type.set_function("step",
+ [] (device_debug &dev, sol::object num)
+ {
+ int steps = 1;
+ if (num.is<int>())
+ steps = num.as<int>();
+ dev.single_step(steps);
+ });
+ device_debug_type.set_function("go", &device_debug::go);
+ device_debug_type.set_function("bpset",
+ [] (device_debug &dev, offs_t address, char const *cond, char const *act)
+ {
+ int result(dev.breakpoint_set(address, cond, act));
+ dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ return result;
+ });
+ device_debug_type["bpclear"] = sol::overload(
+ [] (device_debug &dev, int index)
+ {
+ bool result(dev.breakpoint_clear(index));
+ if (result)
+ {
+ dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ }
+ return result;
+ },
+ [] (device_debug &dev)
+ {
+ dev.breakpoint_clear_all();
+ dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ });
+ device_debug_type.set_function("bpenable", &do_breakpoint_enable<true>);
+ device_debug_type.set_function("bpdisable", &do_breakpoint_enable<false>);
+ device_debug_type.set_function("bplist",
+ [this] (device_debug &dev)
+ {
+ sol::table table = sol().create_table();
+ for (auto const &bpp : dev.breakpoint_list())
+ table[bpp.second->index()] = sol::make_reference(sol(), bpp.second.get());
+ return table;
+ });
+ device_debug_type.set_function("wpset",
+ [] (device_debug &dev, addr_space &sp, std::string const &type, offs_t addr, offs_t len, char const *cond, char const *act)
+ {
+ read_or_write const wptype = s_read_or_write_parser(type);
+ int result(dev.watchpoint_set(sp.space, wptype, addr, len, cond, act));
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return result;
+ });
+ device_debug_type["wpclear"] = sol::overload(
+ [] (device_debug &dev, int index)
+ {
+ bool result(dev.watchpoint_clear(index));
+ if (result)
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return result;
+ },
+ [] (device_debug &dev)
+ {
+ dev.watchpoint_clear_all();
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ });
+ device_debug_type.set_function("wpenable", &do_watchpoint_enable<true>);
+ device_debug_type.set_function("wpdisable", &do_watchpoint_enable<false>);
+ device_debug_type.set_function("wplist",
+ [this] (device_debug &dev, addr_space &sp)
+ {
+ sol::table table = sol().create_table();
+ for (auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
+ table[wpp->index()] = sol::make_reference(sol(), wpp.get());
+ return table;
+ });
+
+
+ auto breakpoint_type = sol().registry().new_usertype<debug_breakpoint>("breakpoint", sol::no_constructor);
+ breakpoint_type["index"] = sol::property(&debug_breakpoint::index);
+ breakpoint_type["enabled"] = sol::property(
+ &debug_breakpoint::enabled,
+ [] (debug_breakpoint &bp, bool val)
+ {
+ if (bp.enabled() != val)
+ {
+ bp.setEnabled(val);
+ bp.debugInterface()->device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ bp.debugInterface()->device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ }
+ });
+ breakpoint_type["address"] = sol::property(&debug_breakpoint::address);
+ breakpoint_type["condition"] = sol::property(&debug_breakpoint::condition);
+ breakpoint_type["action"] = sol::property(&debug_breakpoint::action);
+
+
+ auto watchpoint_type = sol().registry().new_usertype<debug_watchpoint>("watchpoint", sol::no_constructor);
+ watchpoint_type["index"] = sol::property(&debug_watchpoint::index);
+ watchpoint_type["enabled"] = sol::property(
+ &debug_watchpoint::enabled,
+ [] (debug_watchpoint &wp, bool val)
+ {
+ if (wp.enabled() != val)
+ {
+ wp.setEnabled(val);
+ wp.debugInterface()->device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ }
+ });
+ watchpoint_type["type"] = sol::property(
+ [] (debug_watchpoint &wp) -> char const *
+ {
+ switch (wp.type())
+ {
+ case read_or_write::READ:
+ return "r";
+ case read_or_write::WRITE:
+ return "w";
+ case read_or_write::READWRITE:
+ return "rw";
+ default: // huh?
+ return "";
+ }
+ });
+ watchpoint_type["address"] = sol::property(&debug_watchpoint::address);
+ watchpoint_type["length"] = sol::property(&debug_watchpoint::length);
+ watchpoint_type["condition"] = sol::property(&debug_watchpoint::condition);
+ watchpoint_type["action"] = sol::property(&debug_watchpoint::action);
+
+}