From 2c226a3b9fe794fa66b32ba09e6dc34c538118de Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 12 Mar 2023 04:37:30 +1100 Subject: Lua engine: Better bindings for device_state_interface. This avoids creating a table every time the state property of a device is accessed, adds proper support for getting/setting floating/point state entries from Lua, calls the state entry's formatting method to convert to a string (for flags fields, etc.) and exposes more properties. This is a breaking change as the exposed properties on state entries have changed, and the value property has different semantics for floating-point state entries. --- docs/source/techspecs/luareference.rst | 45 ++++++++++++ src/emu/distate.h | 6 +- src/frontend/mame/luaengine.cpp | 127 +++++++++++++++++++++------------ src/frontend/mame/luaengine.ipp | 15 +++- 4 files changed, 142 insertions(+), 51 deletions(-) diff --git a/docs/source/techspecs/luareference.rst b/docs/source/techspecs/luareference.rst index 28ee0c08c75..4c3c5098004 100644 --- a/docs/source/techspecs/luareference.rst +++ b/docs/source/techspecs/luareference.rst @@ -852,6 +852,11 @@ device.debug (read-only) The :ref:`debugger interface ` to the device if it is a CPU device, or ``nil`` if it is not a CPU device or the debugger is not enabled. +device.state[] (read-only) + The :ref:`state entries ` for devices that + expose the register state interface, indexed by symbol, or ``nil`` for other + devices. The index operator and ``index_of`` methods have O(n) complexity; + all other supported operations have O(1) complexity. device.spaces[] (read-only) A table of the device’s :ref:`address spaces `, indexed by name. Only valid for devices that implement the memory @@ -1311,6 +1316,46 @@ slot.options[] (read-only) slot.device (read-only) The underlying :ref:`device `. +.. _luareference-dev-stateentry: + +Device state entry +~~~~~~~~~~~~~~~~~~ + +Wraps MAME’s ``device_state_entry`` class, which allows access to named +registers exposed by a :ref:`device `. Supports +conversion to string for display. + +Instantiation +^^^^^^^^^^^^^ + +manager.machine.devices[tag].state[symbol] + Gets a state entry for a given device by symbol. + +Properties +^^^^^^^^^^ + +entry.value (read/write) + The numeric value of the state entry, as either an integer or floating-point + number. Attempting to set the value of a read-only state entry raises an + error. +entry.symbol (read-only) + The state entry’s symbolic name. +entry.visible (read-only) + A Boolean indicating whether the state entry should be displayed in the + debugger register view. +entry.writeable (read-only) + A Boolean indicating whether it is possible to modify the state entry’s + value. +entry.is_float (read-only) + A Boolean indicating whether the state entry’s value is a floating-point + number. +entry.datamask (read-only) + A bit mask of the valid bits of the value for integer state entries. +entry.datasize (read-only) + The size of the underlying value in bytes for integer state entries. +entry.max_length (read-only) + The maximum display string length for the state entry. + .. _luareference-dev-imagefmt: Media image format diff --git a/src/emu/distate.h b/src/emu/distate.h index 182010c8142..b4727c259f4 100644 --- a/src/emu/distate.h +++ b/src/emu/distate.h @@ -286,12 +286,14 @@ class device_state_interface : public device_interface friend class device_state_entry; public: + using entrylist_type = std::vector >; + // construction/destruction device_state_interface(const machine_config &mconfig, device_t &device); virtual ~device_state_interface(); // configuration access - const std::vector> &state_entries() const { return m_state_list; } + const entrylist_type &state_entries() const { return m_state_list; } // state getters u64 state_int(int index) const { const device_state_entry *entry = state_find_entry(index); return (entry == nullptr) ? 0 : entry->value(); } @@ -361,7 +363,7 @@ protected: static constexpr int FAST_STATE_MAX = 256; // lookups // state - std::vector> m_state_list; // head of state list + entrylist_type m_state_list; // head of state list device_state_entry *m_fast_state[FAST_STATE_MAX + 1 - FAST_STATE_MIN]; // fast access to common entries }; diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index 063f01dc5c9..cca0a7fc972 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -158,6 +158,19 @@ public: } }; + +struct device_state_entries +{ + device_state_entries(device_state_interface const &s) : state(s) { } + device_state_interface::entrylist_type const &items() { return state.state_entries(); } + + static device_state_entry const &unwrap(device_state_interface::entrylist_type::const_iterator const &it) { return **it; } + static int push_key(lua_State *L, device_state_interface::entrylist_type::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, (*it)->symbol()); } + + device_state_interface const &state; +}; + + struct image_interface_formats { image_interface_formats(device_image_interface &i) : image(i) { } @@ -184,9 +197,9 @@ struct plugin_options_plugins } // anonymous namespace -namespace sol -{ +namespace sol { +template <> struct is_container : std::true_type { }; template <> struct is_container : std::true_type { }; template <> struct is_container : std::true_type { }; @@ -294,6 +307,34 @@ public: }; +template <> +struct usertype_container : lua_engine::immutable_sequence_helper +{ +private: + using entrylist_type = device_state_interface::entrylist_type; + +public: + static int get(lua_State *L) + { + device_state_entries &self(get_self(L)); + char const *const symbol(stack::unqualified_get(L)); + auto const found(std::find_if( + self.state.state_entries().begin(), + self.state.state_entries().end(), + [&symbol] (std::unique_ptr const &v) { return !std::strcmp(v->symbol(), symbol); })); + if (self.state.state_entries().end() != found) + return stack::push_reference(L, **found); + else + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } +}; + + template <> struct usertype_container : lua_engine::immutable_sequence_helper { @@ -1406,18 +1447,13 @@ void lua_engine::initialize() } return sp_table; }); - // FIXME: improve this device_type["state"] = sol::property( - [this] (device_t &dev) + [] (device_t &dev, sol::this_state s) -> sol::object { - sol::table st_table = sol().create_table(); - const device_state_interface *state; - if(!dev.interface(state)) - return st_table; - // XXX: refrain from exporting non-visible entries? - for(auto &s : state->state_entries()) - st_table[s->symbol()] = s.get(); - return st_table; + device_state_interface const *state; + if (!dev.interface(state)) + return sol::lua_nil; + return sol::make_object(s, device_state_entries(*state)); }); // FIXME: turn into a wrapper - it's stupid slow to walk on every property access // also, this mixes up things like RAM areas with stuff saved by the device itself, so there's potential for key conflicts @@ -1740,6 +1776,38 @@ void lua_engine::initialize() image_type["device"] = sol::property(static_cast(&device_image_interface::device)); + auto state_entry_type = sol().registry().new_usertype("state_entry", sol::no_constructor); + state_entry_type["value"] = sol::property( + [] (device_state_entry const &entry, sol::this_state s) -> sol::object + { + if (entry.is_float()) + return sol::make_object(s, entry.dvalue()); + else + return sol::make_object(s, entry.value()); + }, + [] (device_state_entry const &entry, sol::this_state s, sol::object value) + { + if (!entry.writeable()) + luaL_error(s, "cannot set value of read-only device state entry"); + else if (entry.is_float()) + entry.set_dvalue(value.as()); + else + entry.set_value(value.as()); + }); + state_entry_type["symbol"] = sol::property(&device_state_entry::symbol); + state_entry_type["visible"] = sol::property(&device_state_entry::visible); + state_entry_type["writeable"] = sol::property(&device_state_entry::writeable); + state_entry_type["is_float"] = sol::property(&device_state_entry::is_float); + state_entry_type["datamask"] = sol::property( + [] (device_state_entry const &entry) + { + return entry.is_float() ? std::optional() : std::optional(entry.datamask()); + }); + state_entry_type["datasize"] = sol::property(&device_state_entry::datasize); + state_entry_type["max_length"] = sol::property(&device_state_entry::max_length); + state_entry_type[sol::meta_function::to_string] = &device_state_entry::to_string; + + auto format_type = sol().registry().new_usertype("image_format", sol::no_constructor); format_type["name"] = sol::property(&image_device_format::name); format_type["description"] = sol::property(&image_device_format::description); @@ -1876,41 +1944,6 @@ void lua_engine::initialize() ui_type["image_display_enabled"] = sol::property(&mame_ui_manager::image_display_enabled, &mame_ui_manager::set_image_display_enabled); -/* device_state_entry library - * - * manager:machine().devices[device_tag].state[state_name] - * - * state:name() - get device state name - * state:is_visible() - is state visible in debugger - * state:is_divider() - is state a divider - * - * state.value - get device state value - */ - - auto dev_state_type = sol().registry().new_usertype("dev_state", "new", sol::no_constructor); - dev_state_type.set("name", &device_state_entry::symbol); - dev_state_type.set("value", sol::property( - [this](device_state_entry &entry) -> uint64_t { - device_state_interface *state = entry.parent_state(); - if(state) - { - machine().save().dispatch_presave(); - return state->state_int(entry.index()); - } - return 0; - }, - [this](device_state_entry &entry, uint64_t val) { - device_state_interface *state = entry.parent_state(); - if(state) - { - state->set_state_int(entry.index(), val); - machine().save().dispatch_presave(); - } - })); - dev_state_type.set("is_visible", &device_state_entry::visible); - dev_state_type.set("is_divider", &device_state_entry::divider); - - /* rom_entry library * * manager:machine().devices[device_tag].roms[rom] diff --git a/src/frontend/mame/luaengine.ipp b/src/frontend/mame/luaengine.ipp index f575f6a8ffb..79c22c3b71d 100644 --- a/src/frontend/mame/luaengine.ipp +++ b/src/frontend/mame/luaengine.ipp @@ -20,6 +20,7 @@ #include #include +#include @@ -460,7 +461,10 @@ protected: result = sol::stack::push(L, i.ix + 1); else result = T::push_key(L, i.it, i.ix); - result += sol::stack::push_reference(L, T::unwrap(i.it)); + if constexpr (std::is_reference_v) + result += sol::stack::push_reference(L, std::ref(T::unwrap(i.it))); + else + result += sol::stack::push_reference(L, T::unwrap(i.it)); ++i; return result; } @@ -481,9 +485,16 @@ public: T &self(immutable_sequence_helper::get_self(L)); std::ptrdiff_t const index(sol::stack::unqualified_get(L, 2)); if ((0 >= index) || (self.items().size() < index)) + { return sol::stack::push(L, sol::lua_nil); + } else - return sol::stack::push_reference(L, T::unwrap(std::next(self.items().begin(), index - 1))); + { + if constexpr (std::is_reference_v) + return sol::stack::push_reference(L, std::ref(T::unwrap(std::next(self.items().begin(), index - 1)))); + else + return sol::stack::push_reference(L, T::unwrap(std::next(self.items().begin(), index - 1))); + } } static int index_of(lua_State *L) -- cgit v1.2.3