mirror of
https://github.com/holub/mame
synced 2025-06-04 03:46:29 +03:00
Lua overhaul [Olivier Galibert, Miodrag Milanovic]
This commit is contained in:
parent
7c59146b4e
commit
05548b8a9c
@ -1460,6 +1460,7 @@ ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value def
|
||||
m_name(name),
|
||||
m_read_param(NULL),
|
||||
m_write_param(NULL),
|
||||
m_digital_value(false),
|
||||
m_min(0),
|
||||
m_max(maskbits),
|
||||
m_sensitivity(0),
|
||||
@ -1493,6 +1494,11 @@ ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value def
|
||||
}
|
||||
}
|
||||
|
||||
void ioport_field::set_value(ioport_value value)
|
||||
{
|
||||
m_digital_value = value != 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ~ioport_field - destructor
|
||||
@ -1896,7 +1902,7 @@ void ioport_field::frame_update(ioport_value &result, bool mouse_down)
|
||||
return;
|
||||
|
||||
// if the state changed, look for switch down/switch up
|
||||
bool curstate = mouse_down || machine().input().seq_pressed(seq());
|
||||
bool curstate = mouse_down || machine().input().seq_pressed(seq()) || m_digital_value;
|
||||
bool changed = false;
|
||||
if (curstate != m_live->last)
|
||||
{
|
||||
|
@ -1014,6 +1014,7 @@ public:
|
||||
ioport_condition &condition() { return m_condition; }
|
||||
ioport_type type() const { return m_type; }
|
||||
UINT8 player() const { return m_player; }
|
||||
void set_value(ioport_value value);
|
||||
|
||||
bool unused() const { return ((m_flags & FIELD_FLAG_UNUSED) != 0); }
|
||||
bool cocktail() const { return ((m_flags & FIELD_FLAG_COCKTAIL) != 0); }
|
||||
@ -1110,6 +1111,9 @@ private:
|
||||
ioport_field_write_delegate m_write; // write callback routine
|
||||
void * m_write_param; // parameter for write callback routine
|
||||
|
||||
// data relevant to digital control types
|
||||
bool m_digital_value; // externally set value
|
||||
|
||||
// data relevant to analog control types
|
||||
ioport_value m_min; // minimum value for absolute axes
|
||||
ioport_value m_max; // maximum value for absolute axes
|
||||
|
@ -39,6 +39,10 @@ static lua_State *globalL = NULL;
|
||||
#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#define luai_writeline() (luai_writestring("\n", 1), fflush(stdout))
|
||||
|
||||
const char *const lua_engine::tname_ioport = "lua.ioport";
|
||||
lua_engine* lua_engine::luaThis = NULL;
|
||||
|
||||
|
||||
static void lstop(lua_State *L, lua_Debug *ar)
|
||||
{
|
||||
(void)ar; /* unused arg. */
|
||||
@ -115,9 +119,89 @@ int lua_engine::incomplete(int status)
|
||||
return 0; /* else... */
|
||||
}
|
||||
|
||||
int emu_gamename(lua_State *L)
|
||||
lua_engine::hook::hook()
|
||||
{
|
||||
lua_pushstring(L, machine_manager::instance()->machine()->system().description);
|
||||
L = NULL;
|
||||
cb = -1;
|
||||
}
|
||||
|
||||
void lua_engine::hook::set(lua_State *_L, int idx)
|
||||
{
|
||||
if (L)
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, cb);
|
||||
|
||||
if (lua_isnil(_L, idx)) {
|
||||
L = NULL;
|
||||
cb = -1;
|
||||
|
||||
} else {
|
||||
L = _L;
|
||||
lua_pushvalue(_L, idx);
|
||||
cb = luaL_ref(_L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
}
|
||||
|
||||
lua_State *lua_engine::hook::precall()
|
||||
{
|
||||
lua_State *T = lua_newthread(L);
|
||||
lua_rawgeti(T, LUA_REGISTRYINDEX, cb);
|
||||
return T;
|
||||
}
|
||||
|
||||
void lua_engine::hook::call(lua_engine *engine, lua_State *T, int nparam)
|
||||
{
|
||||
engine->resume(T, nparam, L);
|
||||
}
|
||||
|
||||
void lua_engine::resume(lua_State *L, int nparam, lua_State *root)
|
||||
{
|
||||
int s = lua_resume(L, NULL, nparam);
|
||||
switch(s) {
|
||||
case LUA_OK:
|
||||
if(!root) {
|
||||
std::map<lua_State *, std::pair<lua_State *, int> >::iterator i = thread_registry.find(L);
|
||||
if(i != thread_registry.end()) {
|
||||
luaL_unref(i->second.first, LUA_REGISTRYINDEX, i->second.second);
|
||||
thread_registry.erase(i);
|
||||
}
|
||||
} else
|
||||
lua_pop(root, 1);
|
||||
break;
|
||||
|
||||
case LUA_YIELD:
|
||||
if(root) {
|
||||
int id = luaL_ref(root, LUA_REGISTRYINDEX);
|
||||
thread_registry[L] = std::pair<lua_State *, int>(root, id);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
osd_printf_error("[LUA ERROR] %s\n", lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void lua_engine::resume(void *_L, INT32 param)
|
||||
{
|
||||
resume(static_cast<lua_State *>(_L));
|
||||
}
|
||||
|
||||
int lua_engine::l_ioport_write(lua_State *L)
|
||||
{
|
||||
ioport_field *field = static_cast<ioport_field *>(getparam(L, 1, tname_ioport));
|
||||
luaL_argcheck(L, lua_isnumber(L, 2), 2, "value expected");
|
||||
field->set_value(lua_tointeger(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// emu_gamename - returns game full name
|
||||
//-------------------------------------------------
|
||||
|
||||
int lua_engine::l_emu_gamename(lua_State *L)
|
||||
{
|
||||
lua_pushstring(L, luaThis->machine().system().description);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -125,50 +209,177 @@ int emu_gamename(lua_State *L)
|
||||
// emu_keypost - post keys to natural keyboard
|
||||
//-------------------------------------------------
|
||||
|
||||
int emu_keypost(lua_State *L)
|
||||
int lua_engine::l_emu_keypost(lua_State *L)
|
||||
{
|
||||
const char *keys = luaL_checkstring(L,1);
|
||||
machine_manager::instance()->machine()->ioport().natkeyboard().post_utf8(keys);
|
||||
luaThis->machine().ioport().natkeyboard().post_utf8(keys);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emu_exit(lua_State *L)
|
||||
int lua_engine::l_emu_time(lua_State *L)
|
||||
{
|
||||
machine_manager::instance()->machine()->schedule_exit();
|
||||
lua_pushnumber(L, luaThis->machine().time().as_double());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emu_start(lua_State *L)
|
||||
void lua_engine::emu_after_done(void *_h, INT32 param)
|
||||
{
|
||||
hook *h = static_cast<hook *>(_h);
|
||||
h->call(this, h->precall(), 0);
|
||||
delete h;
|
||||
}
|
||||
|
||||
int lua_engine::emu_after(lua_State *L)
|
||||
{
|
||||
luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
|
||||
struct hook *h = new hook;
|
||||
h->set(L, 2);
|
||||
machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::emu_after_done), this), 0, h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_engine::l_emu_after(lua_State *L)
|
||||
{
|
||||
return luaThis->emu_after(L);
|
||||
}
|
||||
|
||||
int lua_engine::emu_wait(lua_State *L)
|
||||
{
|
||||
luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
|
||||
machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), this), 0, L);
|
||||
return lua_yieldk(L, 0, 0, 0);
|
||||
}
|
||||
|
||||
int lua_engine::l_emu_wait(lua_State *L)
|
||||
{
|
||||
return luaThis->emu_wait(L);
|
||||
}
|
||||
|
||||
void lua_engine::output_notifier(const char *outname, INT32 value)
|
||||
{
|
||||
if (hook_output_cb.active()) {
|
||||
lua_State *L = hook_output_cb.precall();
|
||||
lua_pushstring(L, outname);
|
||||
lua_pushnumber(L, value);
|
||||
hook_output_cb.call(this, L, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void lua_engine::s_output_notifier(const char *outname, INT32 value, void *param)
|
||||
{
|
||||
static_cast<lua_engine *>(param)->output_notifier(outname, value);
|
||||
}
|
||||
|
||||
void lua_engine::emu_hook_output(lua_State *L)
|
||||
{
|
||||
luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected");
|
||||
hook_output_cb.set(L, 1);
|
||||
|
||||
if (!output_notifier_set) {
|
||||
output_set_notifier(NULL, s_output_notifier, this);
|
||||
output_notifier_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
int lua_engine::l_emu_hook_output(lua_State *L)
|
||||
{
|
||||
luaThis->emu_hook_output(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void *lua_engine::checkparam(lua_State *L, int idx, const char *tname)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
if(!lua_getmetatable(L, idx))
|
||||
return 0;
|
||||
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
name = lua_tostring(L, -1);
|
||||
if(!name || strcmp(name, tname)) {
|
||||
lua_pop(L, 1);
|
||||
return 0;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
return *static_cast<void **>(lua_touserdata(L, idx));
|
||||
}
|
||||
|
||||
void *lua_engine::getparam(lua_State *L, int idx, const char *tname)
|
||||
{
|
||||
void *p = checkparam(L, idx, tname);
|
||||
char msg[256];
|
||||
sprintf(msg, "%s expected", tname);
|
||||
luaL_argcheck(L, p, idx, msg);
|
||||
return p;
|
||||
}
|
||||
|
||||
void lua_engine::push(lua_State *L, void *p, const char *tname)
|
||||
{
|
||||
void **pp = static_cast<void **>(lua_newuserdata(L, sizeof(void *)));
|
||||
*pp = p;
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
int lua_engine::l_emu_exit(lua_State *L)
|
||||
{
|
||||
luaThis->machine().schedule_exit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_engine::l_emu_start(lua_State *L)
|
||||
{
|
||||
const char *system_name = luaL_checkstring(L,1);
|
||||
|
||||
int index = driver_list::find(system_name);
|
||||
if (index != -1) {
|
||||
machine_manager::instance()->schedule_new_driver(driver_list::driver(index));
|
||||
machine_manager::instance()->machine()->schedule_hard_reset();
|
||||
luaThis->machine().schedule_hard_reset();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg emu_funcs [] =
|
||||
{
|
||||
{ "gamename", emu_gamename },
|
||||
{ "keypost", emu_keypost },
|
||||
{ "exit", emu_exit },
|
||||
{ "start", emu_start },
|
||||
{ NULL, NULL } /* sentinel */
|
||||
};
|
||||
|
||||
//-------------------------------------------------
|
||||
// luaopen_emu - connect emu section lib
|
||||
//-------------------------------------------------
|
||||
|
||||
static int luaopen_emu ( lua_State * L )
|
||||
int lua_engine::luaopen_emu(lua_State *L)
|
||||
{
|
||||
static const struct luaL_Reg emu_funcs [] = {
|
||||
{ "gamename", l_emu_gamename },
|
||||
{ "keypost", l_emu_keypost },
|
||||
{ "hook_output", l_emu_hook_output },
|
||||
{ "time", l_emu_time },
|
||||
{ "wait", l_emu_wait },
|
||||
{ "after", l_emu_after },
|
||||
{ "exit", l_emu_exit },
|
||||
{ "start", l_emu_start },
|
||||
{ NULL, NULL } /* sentinel */
|
||||
};
|
||||
|
||||
luaL_newlib(L, emu_funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_engine::luaopen_ioport(lua_State *L)
|
||||
{
|
||||
static const struct luaL_Reg ioport_funcs [] = {
|
||||
{ "write", l_ioport_write },
|
||||
{ NULL, NULL } /* sentinel */
|
||||
};
|
||||
|
||||
luaL_newmetatable(L, tname_ioport);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushstring(L, tname_ioport);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
luaL_setfuncs(L, ioport_funcs, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct msg {
|
||||
astring text;
|
||||
@ -245,14 +456,20 @@ static void *serve_lua(void *param)
|
||||
|
||||
lua_engine::lua_engine()
|
||||
{
|
||||
m_machine = NULL;
|
||||
luaThis = this;
|
||||
m_lua_state = luaL_newstate(); /* create state */
|
||||
output_notifier_set = false;
|
||||
|
||||
luaL_checkversion(m_lua_state);
|
||||
lua_gc(m_lua_state, LUA_GCSTOP, 0); /* stop collector during initialization */
|
||||
luaL_openlibs(m_lua_state); /* open libraries */
|
||||
|
||||
luaopen_lsqlite3(m_lua_state);
|
||||
luaL_requiref(m_lua_state, "emu", luaopen_emu, 1);
|
||||
|
||||
|
||||
luaopen_ioport(m_lua_state);
|
||||
|
||||
lua_gc(m_lua_state, LUA_GCRESTART, 0);
|
||||
msg.ready = 0;
|
||||
msg.status = 0;
|
||||
@ -269,6 +486,29 @@ lua_engine::~lua_engine()
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
void lua_engine::update_machine()
|
||||
{
|
||||
lua_newtable(m_lua_state);
|
||||
if (m_machine!=NULL)
|
||||
{
|
||||
// Create the ioport array
|
||||
ioport_port *port = machine().ioport().first_port();
|
||||
while(port) {
|
||||
ioport_field *field = port->first_field();
|
||||
while(field) {
|
||||
if(field->name()) {
|
||||
push(m_lua_state, field, tname_ioport);
|
||||
lua_setfield(m_lua_state, -2, field->name());
|
||||
}
|
||||
field = field->next();
|
||||
}
|
||||
port = port->next();
|
||||
}
|
||||
}
|
||||
lua_setglobal(m_lua_state, "ioport");
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// initialize - initialize lua hookup to emu engine
|
||||
//-------------------------------------------------
|
||||
@ -326,22 +566,30 @@ void lua_engine::close()
|
||||
// execute - load and execute script
|
||||
//-------------------------------------------------
|
||||
|
||||
void lua_engine::execute(const char *filename)
|
||||
void lua_engine::load_script(const char *filename)
|
||||
{
|
||||
int s = luaL_loadfile(m_lua_state, filename);
|
||||
s = docall(0, 0);
|
||||
report(s);
|
||||
lua_settop(m_lua_state, 0);
|
||||
start();
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// execute_string - execute script from string
|
||||
//-------------------------------------------------
|
||||
|
||||
void lua_engine::execute_string(const char *value)
|
||||
void lua_engine::load_string(const char *value)
|
||||
{
|
||||
int s = luaL_loadstring(m_lua_state, value);
|
||||
s = docall(0, 0);
|
||||
report(s);
|
||||
lua_settop(m_lua_state, 0);
|
||||
start();
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// start - execute the loaded script
|
||||
//-------------------------------------------------
|
||||
|
||||
void lua_engine::start()
|
||||
{
|
||||
resume(m_lua_state);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
||||
#ifndef __LUA_ENGINE_H__
|
||||
#define __LUA_ENGINE_H__
|
||||
|
||||
#include <map>
|
||||
|
||||
struct lua_State;
|
||||
|
||||
class lua_engine
|
||||
@ -27,19 +29,74 @@ public:
|
||||
~lua_engine();
|
||||
|
||||
void initialize();
|
||||
void execute(const char *filename);
|
||||
void execute_string(const char *value);
|
||||
void close();
|
||||
void load_script(const char *filename);
|
||||
void load_string(const char *value);
|
||||
|
||||
void serve_lua();
|
||||
void periodic_check();
|
||||
private:
|
||||
|
||||
void resume(lua_State *L, int nparam = 0, lua_State *root = NULL);
|
||||
void set_machine(running_machine *machine) { m_machine = machine; update_machine(); }
|
||||
private:
|
||||
struct hook {
|
||||
lua_State *L;
|
||||
int cb;
|
||||
|
||||
hook();
|
||||
void set(lua_State *L, int idx);
|
||||
lua_State *precall();
|
||||
void call(lua_engine *engine, lua_State *T, int nparam);
|
||||
bool active() const { return L != NULL; }
|
||||
};
|
||||
|
||||
static const char *const tname_ioport;
|
||||
|
||||
// internal state
|
||||
lua_State *m_lua_state;
|
||||
running_machine * m_machine;
|
||||
|
||||
hook hook_output_cb;
|
||||
bool output_notifier_set;
|
||||
|
||||
static lua_engine* luaThis;
|
||||
|
||||
std::map<lua_State *, std::pair<lua_State *, int> > thread_registry;
|
||||
|
||||
running_machine &machine() const { return *m_machine; }
|
||||
|
||||
void update_machine();
|
||||
void output_notifier(const char *outname, INT32 value);
|
||||
static void s_output_notifier(const char *outname, INT32 value, void *param);
|
||||
|
||||
void emu_after_done(void *_h, INT32 param);
|
||||
int emu_after(lua_State *L);
|
||||
int emu_wait(lua_State *L);
|
||||
void emu_hook_output(lua_State *L);
|
||||
|
||||
static int l_ioport_write(lua_State *L);
|
||||
static int l_emu_after(lua_State *L);
|
||||
static int l_emu_wait(lua_State *L);
|
||||
static int l_emu_time(lua_State *L);
|
||||
static int l_emu_gamename(lua_State *L);
|
||||
static int l_emu_keypost(lua_State *L);
|
||||
static int l_emu_hook_output(lua_State *L);
|
||||
static int l_emu_exit(lua_State *L);
|
||||
static int l_emu_start(lua_State *L);
|
||||
|
||||
void resume(void *L, INT32 param);
|
||||
void report_errors(int status);
|
||||
void start();
|
||||
static int luaopen_emu(lua_State *L);
|
||||
static int luaopen_ioport(lua_State *L);
|
||||
void close();
|
||||
|
||||
static void *checkparam(lua_State *L, int idx, const char *tname);
|
||||
static void *getparam(lua_State *L, int idx, const char *tname);
|
||||
static void push(lua_State *L, void *p, const char *tname);
|
||||
|
||||
int report(int status);
|
||||
int docall(int narg, int nres);
|
||||
int incomplete(int status) ;
|
||||
private:
|
||||
// internal state
|
||||
lua_State* m_lua_state;
|
||||
};
|
||||
|
||||
#endif /* __LUA_ENGINE_H__ */
|
||||
|
@ -195,13 +195,13 @@ const char *running_machine::describe_context()
|
||||
TIMER_CALLBACK_MEMBER(running_machine::autoboot_callback)
|
||||
{
|
||||
if (strlen(options().autoboot_script())!=0) {
|
||||
manager().lua()->execute(options().autoboot_script());
|
||||
manager().lua()->load_script(options().autoboot_script());
|
||||
}
|
||||
if (strlen(options().autoboot_command())!=0) {
|
||||
else if (strlen(options().autoboot_command())!=0) {
|
||||
astring cmd = astring(options().autoboot_command());
|
||||
cmd.replace("'","\\'");
|
||||
astring val = astring("emu.keypost('",cmd,"')");
|
||||
manager().lua()->execute_string(val);
|
||||
manager().lua()->load_string(val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,6 +292,8 @@ void running_machine::start()
|
||||
|
||||
// allocate autoboot timer
|
||||
m_autoboot_timer = scheduler().timer_alloc(timer_expired_delegate(FUNC(running_machine::autoboot_callback), this));
|
||||
|
||||
manager().update_machine();
|
||||
}
|
||||
|
||||
|
||||
|
@ -148,6 +148,12 @@ void machine_manager::schedule_new_driver(const game_driver &driver)
|
||||
/***************************************************************************
|
||||
CORE IMPLEMENTATION
|
||||
***************************************************************************/
|
||||
void machine_manager::update_machine()
|
||||
{
|
||||
m_lua.set_machine(m_machine);
|
||||
m_web.set_machine(m_machine);
|
||||
if (m_machine!=NULL) m_web.push_message("update_machine");
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
execute - run the core emulation
|
||||
@ -205,9 +211,6 @@ int machine_manager::execute()
|
||||
|
||||
set_machine(&machine);
|
||||
|
||||
m_web.set_machine(machine);
|
||||
m_web.push_message("update_machine");
|
||||
|
||||
// run the machine
|
||||
error = machine.run(firstrun);
|
||||
firstrun = false;
|
||||
|
@ -97,6 +97,8 @@ public:
|
||||
|
||||
void set_machine(running_machine *machine) { m_machine = machine; }
|
||||
|
||||
void update_machine();
|
||||
|
||||
/* execute as configured by the OPTION_SYSTEMNAME option on the specified options */
|
||||
int execute();
|
||||
void schedule_new_driver(const game_driver &driver);
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
void push_message(const char *message);
|
||||
void close();
|
||||
|
||||
void set_machine(running_machine &machine) { m_machine = &machine; }
|
||||
void set_machine(running_machine *machine) { m_machine = machine; }
|
||||
int begin_request_handler(struct mg_connection *conn);
|
||||
protected:
|
||||
// getters
|
||||
|
Loading…
Reference in New Issue
Block a user