summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2020-11-30 12:01:14 +1100
committer Vas Crabb <vas@vastheman.com>2020-11-30 12:01:14 +1100
commit1bdf8d272a32d161e11c96984bf3614de3210003 (patch)
treeaca314836043ffb5303de52ceaba860bf0860133 /src
parentf3454cee2f5fbfff068f53c3716ba187ea4b1492 (diff)
frontend: Lua engine improvements.
Added methods for enabling and disabling breakpoints and watchpoints, and made debugger views update when breakpoints/watchpoints are manipulated from Lua. Made breakpoints and watchpoints objects rather than tables. (It’s not possible to enable/disable a breakpoint or watchpoint from the object itself, you have to go through its owners' debug interface.) Exposed more device_t members for dealing with child/sibling tags and devices. Also provided a way to get regions/shares/banks from a device using relative tags rather than going through the memory manager with absolute tags.
Diffstat (limited to 'src')
-rw-r--r--src/emu/debug/debugcpu.cpp3
-rw-r--r--src/emu/debug/points.h2
-rw-r--r--src/frontend/mame/luaengine.cpp299
-rw-r--r--src/frontend/mame/luaengine.h1
-rw-r--r--src/frontend/mame/luaengine_debug.cpp314
5 files changed, 409 insertions, 210 deletions
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp
index 3c325d3a841..e91a7801b3c 100644
--- a/src/emu/debug/debugcpu.cpp
+++ b/src/emu/debug/debugcpu.cpp
@@ -1669,7 +1669,7 @@ void device_debug::breakpoint_update_flags()
break;
}
- if ( ! ( m_flags & DEBUG_FLAG_LIVE_BP ) )
+ if (!(m_flags & DEBUG_FLAG_LIVE_BP))
{
// see if there are any enabled registerpoints
for (debug_registerpoint &rp : *m_rplist)
@@ -1677,6 +1677,7 @@ void device_debug::breakpoint_update_flags()
if (rp.m_enabled)
{
m_flags |= DEBUG_FLAG_LIVE_BP;
+ break;
}
}
}
diff --git a/src/emu/debug/points.h b/src/emu/debug/points.h
index 220b75728ce..84cdad7720b 100644
--- a/src/emu/debug/points.h
+++ b/src/emu/debug/points.h
@@ -47,7 +47,7 @@ public:
const char *action() const { return m_action.c_str(); }
// setters
- void setEnabled(bool value) { m_enabled = value; }
+ void setEnabled(bool value) { m_enabled = value; } // FIXME: need to update breakpoint flags but it's a private method
private:
// internals
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index 48ebc897899..dd03ca08cc5 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -16,10 +16,6 @@
#include "ui/pluginopt.h"
#include "ui/ui.h"
-#include "debug/debugcon.h"
-#include "debug/debugcpu.h"
-#include "debug/points.h"
-#include "debug/textbuf.h"
#include "debugger.h"
#include "drivenum.h"
#include "emuopts.h"
@@ -525,15 +521,6 @@ void lua_engine::initialize()
};
- 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<ui::text_layout::text_justify, 3> s_text_justify_parser =
{
{ "left", ui::text_layout::LEFT },
@@ -1165,7 +1152,7 @@ void lua_engine::initialize()
machine_type["logerror"] = [] (running_machine &m, const char *str) { m.logerror("[luaengine] %s\n", str); };
-/* game_driver library
+/* game_driver library
*
* emu.driver_find(driver_name)
*
@@ -1191,203 +1178,90 @@ void lua_engine::initialize()
* driver.is_incomplete - official system with blatantly incomplete hardware/software
*/
- auto game_driver_type = sol().registry().new_usertype<game_driver>("game_driver", "new", sol::no_constructor);
- game_driver_type.set("source_file", sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; }));
- game_driver_type.set("parent", sol::readonly(&game_driver::parent));
- game_driver_type.set("name", sol::property([] (game_driver const &driver) { return &driver.name[0]; }));
- game_driver_type.set("description", sol::property([] (game_driver const &driver) { return &driver.type.fullname()[0]; }));
- game_driver_type.set("year", sol::readonly(&game_driver::year));
- game_driver_type.set("manufacturer", sol::readonly(&game_driver::manufacturer));
- game_driver_type.set("compatible_with", sol::readonly(&game_driver::compatible_with));
- game_driver_type.set("default_layout", sol::readonly(&game_driver::default_layout));
- game_driver_type.set("orientation", sol::property([](game_driver const &driver) {
- std::string rot;
- switch (driver.flags & machine_flags::MASK_ORIENTATION)
- {
- case machine_flags::ROT0:
- rot = "rot0";
- break;
- case machine_flags::ROT90:
- rot = "rot90";
- break;
- case machine_flags::ROT180:
- rot = "rot180";
- break;
- case machine_flags::ROT270:
- rot = "rot270";
- break;
- default:
- rot = "undefined";
- break;
- }
- return rot;
- }));
- game_driver_type.set("type", sol::property([](game_driver const &driver) {
- std::string type;
- switch (driver.flags & machine_flags::MASK_TYPE)
- {
- case machine_flags::TYPE_ARCADE:
- type = "arcade";
- break;
- case machine_flags::TYPE_CONSOLE:
- type = "console";
- break;
- case machine_flags::TYPE_COMPUTER:
- type = "computer";
- break;
- default:
- type = "other";
- break;
- }
- return type;
- }));
- game_driver_type.set("not_working", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) > 0; }));
- game_driver_type.set("supports_save", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) > 0; }));
- game_driver_type.set("no_cocktail", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) > 0; }));
- game_driver_type.set("is_bios_root", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) > 0; }));
- game_driver_type.set("requires_artwork", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) > 0; }));
- game_driver_type.set("clickable_artwork", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) > 0; }));
- game_driver_type.set("unofficial", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) > 0; }));
- game_driver_type.set("no_sound_hw", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) > 0; }));
- game_driver_type.set("mechanical", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) > 0; }));
- game_driver_type.set("is_incomplete", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::IS_INCOMPLETE) > 0; } ));
-
-
-/* debugger_manager library (requires debugger to be active)
- *
- * manager:machine():debugger()
- *
- * debugger:command(command_string) - run command_string in debugger console
- *
- * debugger.consolelog[] - get consolelog text buffer (wrap_textbuf)
- * debugger.errorlog[] - get errorlog text buffer (wrap_textbuf)
- * debugger.visible_cpu - accessor for debugger active cpu for commands, affects debug views
- * debugger.execution_state - accessor for active cpu run state
- */
-
- struct wrap_textbuf { wrap_textbuf(const text_buffer &buf) : textbuf(buf) { } std::reference_wrapper<const text_buffer> textbuf; };
-
- auto debugger_type = sol().registry().new_usertype<debugger_manager>("debugger", "new", sol::no_constructor);
- debugger_type.set("command", [](debugger_manager &debug, const std::string &cmd) { debug.console().execute_command(cmd, false); });
- debugger_type.set("consolelog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); }));
- debugger_type.set("errorlog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); }));
- debugger_type.set("visible_cpu", sol::property(
- [](debugger_manager &debug) { debug.console().get_visible_cpu(); },
- [](debugger_manager &debug, device_t &dev) { debug.console().set_visible_cpu(&dev); }));
- debugger_type.set("execution_state", sol::property(
- [](debugger_manager &debug) {
- return debug.cpu().is_stopped() ? "stop" : "run";
- },
- [](debugger_manager &debug, const std::string &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; });
-
-/* device_debug library (requires debugger to be active)
- *
- * manager:machine().devices[device_tag]:debug()
- *
- * debug:step([opt] steps) - run cpu steps, default 1
- * debug:go() - run cpu
- * debug:bpset(addr, [opt] cond, [opt] act) - set breakpoint on addr, cond and act are debugger
- * expressions. returns breakpoint index
- * debug:bpclr(idx) - clear break
- * debug:bplist()[] - table of breakpoints (k=index, v=debug_breakpoint)
- * debug:wpset(space, type, addr, len, [opt] cond, [opt] act) - set watchpoint, cond and act
- * are debugger expressions.
- * returns watchpoint index
- * debug:wpclr(idx) - clear watch
- * debug:wplist(space)[] - table of watchpoints (k=index, v=watchpoint)
- */
-
- auto device_debug_type = sol().registry().new_usertype<device_debug>("device_debug", "new", sol::no_constructor);
- device_debug_type.set("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("go", &device_debug::go);
- device_debug_type.set("bpset", [](device_debug &dev, offs_t addr, const char *cond, const char *act) { return dev.breakpoint_set(addr, cond, act); });
- device_debug_type.set("bpclr", &device_debug::breakpoint_clear);
- device_debug_type.set("bplist", [this](device_debug &dev) {
- sol::table table = sol().create_table();
- for(const auto &bpp : dev.breakpoint_list())
+ auto game_driver_type = sol().registry().new_usertype<game_driver>("game_driver", sol::no_constructor);
+ game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; });
+ game_driver_type["parent"] = sol::readonly(&game_driver::parent);
+ game_driver_type["name"] = sol::property([] (game_driver const &driver) { return &driver.name[0]; });
+ game_driver_type["description"] = sol::property([] (game_driver const &driver) { return &driver.type.fullname()[0]; });
+ game_driver_type["year"] = sol::readonly(&game_driver::year);
+ game_driver_type["manufacturer"] = sol::readonly(&game_driver::manufacturer);
+ game_driver_type["compatible_with"] = sol::readonly(&game_driver::compatible_with);
+ game_driver_type["default_layout"] = sol::readonly(&game_driver::default_layout);
+ game_driver_type["orientation"] = sol::property(
+ [] (game_driver const &driver)
{
- const debug_breakpoint &bpt = *bpp.second;
- sol::table bp = sol().create_table();
- bp["enabled"] = bpt.enabled();
- bp["address"] = bpt.address();
- bp["condition"] = bpt.condition();
- bp["action"] = bpt.action();
- table[bpt.index()] = bp;
- }
- return table;
- });
- device_debug_type.set("wpset", [](device_debug &dev, addr_space &sp, const std::string &type, offs_t addr, offs_t len, const char *cond, const char *act) {
- read_or_write wptype = s_read_or_write_parser(type);
- return dev.watchpoint_set(sp.space, wptype, addr, len, cond, act);
- });
- device_debug_type.set("wpclr", &device_debug::watchpoint_clear);
- device_debug_type.set("wplist", [this](device_debug &dev, addr_space &sp) {
- sol::table table = sol().create_table();
- for(auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
+ std::string rot;
+ switch (driver.flags & machine_flags::MASK_ORIENTATION)
+ {
+ case machine_flags::ROT0:
+ rot = "rot0";
+ break;
+ case machine_flags::ROT90:
+ rot = "rot90";
+ break;
+ case machine_flags::ROT180:
+ rot = "rot180";
+ break;
+ case machine_flags::ROT270:
+ rot = "rot270";
+ break;
+ default:
+ rot = "undefined";
+ break;
+ }
+ return rot;
+ });
+ game_driver_type["type"] = sol::property(
+ [](game_driver const &driver)
{
- sol::table wp = sol().create_table();
- wp["enabled"] = wpp->enabled();
- wp["address"] = wpp->address();
- wp["length"] = wpp->length();
- switch(wpp->type())
+ std::string type;
+ switch (driver.flags & machine_flags::MASK_TYPE)
{
- case read_or_write::READ:
- wp["type"] = "r";
- break;
- case read_or_write::WRITE:
- wp["type"] = "w";
- break;
- case read_or_write::READWRITE:
- wp["type"] = "rw";
- break;
- default: // huh?
- wp["type"] = "";
- break;
+ case machine_flags::TYPE_ARCADE:
+ type = "arcade";
+ break;
+ case machine_flags::TYPE_CONSOLE:
+ type = "console";
+ break;
+ case machine_flags::TYPE_COMPUTER:
+ type = "computer";
+ break;
+ default:
+ type = "other";
+ break;
}
- wp["condition"] = wpp->condition();
- wp["action"] = wpp->action();
- table[wpp->index()] = wp;
- }
- return table;
- });
-
-
-/* device_t library
+ return type;
+ });
+ game_driver_type["not_working"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) != 0; });
+ game_driver_type["supports_save"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) != 0; });
+ game_driver_type["no_cocktail"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) != 0; });
+ game_driver_type["is_bios_root"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) != 0; });
+ game_driver_type["requires_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) != 0; });
+ game_driver_type["clickable_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) != 0; });
+ game_driver_type["unofficial"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) != 0; });
+ game_driver_type["no_sound_hw"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) != 0; });
+ game_driver_type["mechanical"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) != 0; });
+ game_driver_type["is_incomplete"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_INCOMPLETE) != 0; });
+
+
+/* device_t library
*
* manager: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:subtag(tag) - get absolute tag relative to this device
+ * device:siblingtag(tag) - get absolute tag relative to this device
+ * device:memregion(tag) - get memory region
+ * device:memshare(tag) - get memory share
+ * device:membank(tag) - get memory bank
+ * device:subdevice(tag) - get subdevice
+ * device:siblingdevice(tag) - get sibling device
+ * device:debug() - debug interface, CPUs only
*
+ * device.tag - device tree tag
+ * device.basetag - last component of tag ("root" for root device)
+ * device.name - device type full name
+ * device.shortname - device type short name
+ * device.owner - parent device (nil for root device)
* device.configured - whether configuration is complete
* device.started - whether the device has been started
* device.spaces[] - device address spaces table (k=name, v=addr_space)
@@ -1396,11 +1270,14 @@ void lua_engine::initialize()
* device.roms[] - device rom entry table (k=name, v=rom_entry)
*/
- auto device_type = sol().registry().new_usertype<device_t>("device", "new", sol::no_constructor);
- device_type["name"] = &device_t::name;
- device_type["shortname"] = &device_t::shortname;
- device_type["tag"] = &device_t::tag;
- device_type["owner"] = &device_t::owner;
+ auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor);
+ device_type["subtag"] = &device_t::subtag;
+ device_type["siblingtag"] = &device_t::siblingtag;
+ device_type["memregion"] = &device_t::memregion;
+ device_type["memshare"] = &device_t::memshare;
+ device_type["membank"] = &device_t::membank;
+ device_type["subdevice"] = static_cast<device_t *(device_t::*)(char const *) const>(&device_t::subdevice);
+ device_type["siblingdevice"] = static_cast<device_t *(device_t::*)(char const *) const>(&device_t::siblingdevice);
device_type["debug"] =
[this] (device_t &dev) -> sol::object
{
@@ -1408,6 +1285,11 @@ void lua_engine::initialize()
return sol::make_object(sol(), sol::lua_nil);
return sol::make_object(sol(), dev.debug());
};
+ device_type["tag"] = sol::property(&device_t::tag);
+ device_type["basetag"] = sol::property(&device_t::basetag);
+ device_type["name"] = sol::property(&device_t::name);
+ device_type["shortname"] = sol::property(&device_t::shortname);
+ device_type["owner"] = sol::property(&device_t::owner);
device_type["configured"] = sol::property(&device_t::configured);
device_type["started"] = sol::property(&device_t::started);
device_type["spaces"] = sol::property(
@@ -1939,6 +1821,7 @@ void lua_engine::initialize()
// set up other user types
+ initialize_debug();
initialize_input();
initialize_memory();
initialize_render();
diff --git a/src/frontend/mame/luaengine.h b/src/frontend/mame/luaengine.h
index 3053de34b7d..e479ba60646 100644
--- a/src/frontend/mame/luaengine.h
+++ b/src/frontend/mame/luaengine.h
@@ -169,6 +169,7 @@ private:
template<typename TFunc, typename... TArgs>
sol::protected_function_result invoke(TFunc &&func, TArgs&&... args);
+ void initialize_debug();
void initialize_input();
void initialize_memory();
void initialize_render();
diff --git a/src/frontend/mame/luaengine_debug.cpp b/src/frontend/mame/luaengine_debug.cpp
new file mode 100644
index 00000000000..8fa89e23754
--- /dev/null
+++ b/src/frontend/mame/luaengine_debug.cpp
@@ -0,0 +1,314 @@
+// 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/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;
+};
+
+
+sol::object do_breakpoint_enable(lua_State *L, device_debug &dev, sol::object &index, bool enable)
+{
+ 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(L, result);
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call bpenable with integer or nil\n");
+ return sol::lua_nil;
+ }
+}
+
+
+sol::object do_watchpoint_enable(lua_State *L, device_debug &dev, sol::object &index, bool enable)
+{
+ 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(L, result);
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call wpenable with integer or nil");
+ return sol::lua_nil;
+ }
+}
+
+} // anonymous namespace
+
+
+void lua_engine::initialize_debug()
+{
+
+ 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 }
+ };
+
+
+/* debugger_manager library (requires debugger to be active)
+ *
+ * manager:machine():debugger()
+ *
+ * debugger:command(command_string) - run command_string in debugger console
+ *
+ * debugger.consolelog[] - get consolelog text buffer (wrap_textbuf)
+ * debugger.errorlog[] - get errorlog text buffer (wrap_textbuf)
+ * debugger.visible_cpu - accessor for debugger active cpu for commands, affects debug views
+ * debugger.execution_state - accessor for active cpu run state
+ */
+
+ auto debugger_type = sol().registry().new_usertype<debugger_manager>("debugger", sol::no_constructor);
+ debugger_type["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) { 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; });
+
+
+/* device_debug library (requires debugger to be active)
+ *
+ * manager:machine().devices[device_tag]:debug()
+ *
+ * debug:step([opt] steps) - run cpu steps, default 1
+ * debug:go() - run cpu
+ * debug:bpset(addr, [opt] cond, [opt] act) - set breakpoint on addr, cond and act are debugger
+ * expressions. returns breakpoint index
+ * debug:bpclr(idx) - clear breakpoint
+ * debug:bpenable(idx) - enable breakpoint
+ * debug:bpdisable(idx) - disable breakpoint
+ * debug:bplist()[] - table of breakpoints (k=index, v=debug_breakpoint)
+ * debug:wpset(space, type, addr, len, [opt] cond, [opt] act) - set watchpoint, cond and act
+ * are debugger expressions.
+ * returns watchpoint index
+ * debug:wpclr(idx) - clear watchpoint
+ * debug:wpenable(idx) - enable watchpoint
+ * debug:wpdisable(idx) - disable watchpoint
+ * debug:wplist(space)[] - table of watchpoints (k=index, v=debug_watchpoint)
+ */
+
+ auto device_debug_type = sol().registry().new_usertype<device_debug>("device_debug", sol::no_constructor);
+ device_debug_type["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["go"] = &device_debug::go;
+ device_debug_type["bpset"] =
+ [this] (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"] =
+ [this] (device_debug &dev, sol::object index) -> sol::object
+ {
+ if (index == sol::lua_nil)
+ {
+ dev.breakpoint_clear_all();
+ 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_clear(index.as<int>()));
+ 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(sol(), result);
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call bpclear with integer or nil");
+ return sol::lua_nil;
+ }
+ };
+ device_debug_type["bpenable"] = [this] (device_debug &dev, sol::object index) { return do_breakpoint_enable(sol(), dev, index, true); };
+ device_debug_type["bpdisable"] = [this] (device_debug &dev, sol::object index) { return do_breakpoint_enable(sol(), dev, index, false); };
+ device_debug_type["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);
+ return table;
+ };
+ device_debug_type["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 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"] =
+ [this] (device_debug &dev, sol::object index) -> sol::object
+ {
+ if (index == sol::lua_nil)
+ {
+ dev.watchpoint_clear_all();
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return sol::lua_nil;
+ }
+ else if (index.is<int>())
+ {
+ bool result(dev.watchpoint_clear(index.as<int>()));
+ if (result)
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return sol::make_object(sol(), result);
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call wpclear with integer or nil");
+ return sol::lua_nil;
+ }
+ };
+ device_debug_type["wpenable"] = [this] (device_debug &dev, sol::object index) { return do_watchpoint_enable(sol(), dev, index, true); };
+ device_debug_type["wpdisable"] = [this] (device_debug &dev, sol::object index) { return do_watchpoint_enable(sol(), dev, index, false); };
+ device_debug_type["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);
+ return table;
+ };
+
+
+/* debug_breakpoint library (requires debugger to be active)
+ *
+ * manager:machine().devices[device_tag]:debug():bplist()[index]
+ *
+ * breakpoint.index - stable index of breakpoint
+ * breakpoint.enabled - whether breakpoint is enabled
+ * breakpoint.address -
+ * breakpoint.condition -
+ * breakpoint.action -
+ */
+
+ 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);
+ breakpoint_type["address"] = sol::property(&debug_breakpoint::address);
+ breakpoint_type["condition"] = sol::property(&debug_breakpoint::condition);
+ breakpoint_type["action"] = sol::property(&debug_breakpoint::action);
+
+
+/* debug_watchpoint library (requires debugger to be active)
+ *
+ * manager:machine().devices[device_tag]:debug():wplist()[index]
+ *
+ * watchpoint.index - stable index of watchpoint
+ * watchpoint.enabled - whether breakpoint is enabled
+ * watchpoint.type - type of access to watch ("r", "w" or "rw")
+ * watchpoint.address - start of address range to watch
+ * watchpoint.length - lenght of address range to watch
+ * watchpoint.condition -
+ * watchpoint.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);
+ 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);
+
+}