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.
This commit is contained in:
Vas Crabb 2023-03-12 04:37:30 +11:00
parent bff2c33fee
commit 2c226a3b9f
4 changed files with 142 additions and 51 deletions

View File

@ -852,6 +852,11 @@ device.debug (read-only)
The :ref:`debugger interface <luareference-debug-devdebug>` 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 <luareference-dev-stateentry>` 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 devices :ref:`address spaces <luareference-mem-space>`,
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-device>`.
.. _luareference-dev-stateentry:
Device state entry
~~~~~~~~~~~~~~~~~~
Wraps MAMEs ``device_state_entry`` class, which allows access to named
registers exposed by a :ref:`device <luareference-dev-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 entrys 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 entrys
value.
entry.is_float (read-only)
A Boolean indicating whether the state entrys 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

View File

@ -286,12 +286,14 @@ class device_state_interface : public device_interface
friend class device_state_entry;
public:
using entrylist_type = std::vector<std::unique_ptr<device_state_entry> >;
// construction/destruction
device_state_interface(const machine_config &mconfig, device_t &device);
virtual ~device_state_interface();
// configuration access
const std::vector<std::unique_ptr<device_state_entry>> &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<std::unique_ptr<device_state_entry>> 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
};

View File

@ -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<device_state_entries> : std::true_type { };
template <> struct is_container<image_interface_formats> : std::true_type { };
template <> struct is_container<plugin_options_plugins> : std::true_type { };
@ -294,6 +307,34 @@ public:
};
template <>
struct usertype_container<device_state_entries> : lua_engine::immutable_sequence_helper<device_state_entries, device_state_interface::entrylist_type const, device_state_interface::entrylist_type::const_iterator>
{
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<char const *>(L));
auto const found(std::find_if(
self.state.state_entries().begin(),
self.state.state_entries().end(),
[&symbol] (std::unique_ptr<device_state_entry> 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<image_interface_formats> : lua_engine::immutable_sequence_helper<image_interface_formats, device_image_interface::formatlist_type const, device_image_interface::formatlist_type::const_iterator>
{
@ -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_t & (device_image_interface::*)()>(&device_image_interface::device));
auto state_entry_type = sol().registry().new_usertype<device_state_entry>("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<double>());
else
entry.set_value(value.as<u64>());
});
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<u64>() : std::optional<u64>(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_device_format>("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<device_state_entry>("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]

View File

@ -20,6 +20,7 @@
#include <cassert>
#include <system_error>
#include <type_traits>
@ -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<decltype(T::unwrap(i.it))>)
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<std::ptrdiff_t>(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<decltype(T::unwrap(std::next(self.items().begin(), index - 1)))>)
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)