// license:BSD-3-Clause // copyright-holders:Miodrag Milanovic,Luca Bruno /*************************************************************************** luaengine.c Controls execution of the core MAME system. ***************************************************************************/ #include #include "lua.hpp" #include "luabridge/Source/LuaBridge/LuaBridge.h" #include #include "emu.h" #include "emuopts.h" #include "osdepend.h" #include "drivenum.h" #include "ui/ui.h" #include "mongoose/mongoose.h" //************************************************************************** // LUA ENGINE //************************************************************************** #if !defined(LUA_PROMPT) #define LUA_PROMPT "> " #define LUA_PROMPT2 ">> " #endif #if !defined(LUA_MAXINPUT) #define LUA_MAXINPUT 512 #endif #define lua_readline(b,p) \ (fputs(p, stdout), fflush(stdout), /* show prompt */ \ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ 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; extern "C" { int luaopen_lsqlite3(lua_State *L); } static void lstop(lua_State *L, lua_Debug *ar) { (void)ar; /* unused arg. */ lua_sethook(L, NULL, 0, 0); luaL_error(L, "interrupted!"); } static void laction(int i) { signal(i, SIG_DFL); /* if another SIGINT happens before lstop, terminate process (default action) */ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); } int lua_engine::report(int status) { if (status != LUA_OK && !lua_isnil(m_lua_state, -1)) { const char *msg = lua_tostring(m_lua_state, -1); if (msg == NULL) msg = "(error object is not a string)"; lua_writestringerror("%s\n", msg); lua_pop(m_lua_state, 1); /* force a complete garbage collection in case of errors */ lua_gc(m_lua_state, LUA_GCCOLLECT, 0); } return status; } static int traceback (lua_State *L) { const char *msg = lua_tostring(L, 1); if (msg) luaL_traceback(L, L, msg, 1); else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ lua_pushliteral(L, "(no error message)"); } return 1; } int lua_engine::docall(int narg, int nres) { int status; int base = lua_gettop(m_lua_state) - narg; /* function index */ lua_pushcfunction(m_lua_state, traceback); /* push traceback function */ lua_insert(m_lua_state, base); /* put it under chunk and args */ globalL = m_lua_state; /* to be available to 'laction' */ signal(SIGINT, laction); status = lua_pcall(m_lua_state, narg, nres, base); signal(SIGINT, SIG_DFL); lua_remove(m_lua_state, base); /* remove traceback function */ return status; } /* mark in error messages for incomplete statements */ #define EOFMARK "" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) int lua_engine::incomplete(int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(m_lua_state, -1, &lmsg); if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { lua_pop(m_lua_state, 1); return 1; } } return 0; /* else... */ } lua_engine::hook::hook() { L = NULL; cb = -1; } #ifdef SDLMAME_SOLARIS #undef _L #endif 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 >::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(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(_L)); } int lua_engine::l_ioport_write(lua_State *L) { ioport_field *field = static_cast(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_app_name - return application name //------------------------------------------------- int lua_engine::l_emu_app_name(lua_State *L) { lua_pushstring(L, emulator_info::get_appname_lower()); return 1; } //------------------------------------------------- // emu_app_version - return application version //------------------------------------------------- int lua_engine::l_emu_app_version(lua_State *L) { lua_pushstring(L, bare_build_version); return 1; } //------------------------------------------------- // emu_gamename - returns game full name //------------------------------------------------- int lua_engine::l_emu_gamename(lua_State *L) { lua_pushstring(L, luaThis->machine().system().description); return 1; } //------------------------------------------------- // emu_romname - returns rom base name //------------------------------------------------- int lua_engine::l_emu_romname(lua_State *L) { lua_pushstring(L, luaThis->machine().basename()); return 1; } //------------------------------------------------- // emu_pause/emu_unpause - pause/unpause game //------------------------------------------------- int lua_engine::l_emu_pause(lua_State *L) { luaThis->machine().pause(); return 0; } int lua_engine::l_emu_unpause(lua_State *L) { luaThis->machine().resume(); return 0; } //------------------------------------------------- // emu_keypost - post keys to natural keyboard //------------------------------------------------- int lua_engine::l_emu_keypost(lua_State *L) { const char *keys = luaL_checkstring(L,1); luaThis->machine().ioport().natkeyboard().post_utf8(keys); return 1; } int lua_engine::l_emu_time(lua_State *L) { lua_pushnumber(L, luaThis->machine().time().as_double()); return 1; } void lua_engine::emu_after_done(void *_h, INT32 param) { hook *h = static_cast(_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(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; } int lua_engine::l_emu_set_hook(lua_State *L) { luaThis->emu_set_hook(L); return 0; } void lua_engine::emu_set_hook(lua_State *L) { luaL_argcheck(L, lua_isfunction(L, 1) || lua_isnil(L, 1), 1, "callback function expected"); luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected"); const char *hookname = luaL_checkstring(L,2); if (strcmp(hookname, "output") == 0) { hook_output_cb.set(L, 1); if (!output_notifier_set) { output_set_notifier(NULL, s_output_notifier, this); output_notifier_set = true; } } else if (strcmp(hookname, "frame") == 0) { hook_frame_cb.set(L, 1); } else { lua_writestringerror("%s", "Unknown hook name, aborting.\n"); } } //------------------------------------------------- // machine_get_screens - return table of available screens userdata // -> manager:machine().screens[":screen"] //------------------------------------------------- luabridge::LuaRef lua_engine::l_machine_get_screens(const running_machine *r) { lua_State *L = luaThis->m_lua_state; luabridge::LuaRef screens_table = luabridge::LuaRef::newTable(L); for (device_t *dev = r->first_screen(); dev != NULL; dev = dev->next()) { screen_device *sc = dynamic_cast(dev); if (sc && sc->configured() && sc->started() && sc->type()) { screens_table[sc->tag()] = sc; } } return screens_table; } //------------------------------------------------- // machine_get_devices - return table of available devices userdata // -> manager:machine().devices[":maincpu"] //------------------------------------------------- luabridge::LuaRef lua_engine::l_machine_get_devices(const running_machine *r) { running_machine *m = const_cast(r); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef devs_table = luabridge::LuaRef::newTable(L); device_t *root = &(m->root_device()); devs_table = devtree_dfs(root, devs_table); return devs_table; } // private helper for get_devices - DFS visit all devices in a running machine luabridge::LuaRef lua_engine::devtree_dfs(device_t *root, luabridge::LuaRef devs_table) { if (root) { for (device_t *dev = root->first_subdevice(); dev != NULL; dev = dev->next()) { if (dev && dev->configured() && dev->started()) { devs_table[dev->tag()] = dev; devtree_dfs(dev, devs_table); } } } return devs_table; } //------------------------------------------------- // device_get_memspaces - return table of available address spaces userdata // -> manager:machine().devices[":maincpu"].spaces["program"] //------------------------------------------------- luabridge::LuaRef lua_engine::l_dev_get_memspaces(const device_t *d) { device_t *dev = const_cast(d); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef sp_table = luabridge::LuaRef::newTable(L); for (address_spacenum sp = AS_0; sp < ADDRESS_SPACES; sp++) { if (dev->memory().has_space(sp)) { sp_table[dev->memory().space(sp).name()] = &(dev->memory().space(sp)); } } return sp_table; } //------------------------------------------------- // device_get_state - return table of available state userdata // -> manager:machine().devices[":maincpu"].state //------------------------------------------------- luabridge::LuaRef lua_engine::l_dev_get_states(const device_t *d) { device_t *dev = const_cast(d); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef st_table = luabridge::LuaRef::newTable(L); for (const device_state_entry *s = dev->state().state_first(); s != NULL; s = s->next()) { // XXX: refrain from exporting non-visible entries? if (s) { st_table[s->symbol()] = const_cast(s); } } return st_table; } //------------------------------------------------- // state_get_value - return value of a device state entry // -> manager:machine().devices[":maincpu"].state["PC"].value //------------------------------------------------- UINT64 lua_engine::l_state_get_value(const device_state_entry *d) { device_state_interface *state = d->parent_state(); if(state) { luaThis->machine().save().dispatch_presave(); return state->state_int(d->index()); } else { return 0; } } //------------------------------------------------- // state_set_value - set value of a device state entry // -> manager:machine().devices[":maincpu"].state["D0"].value = 0x0c00 //------------------------------------------------- void lua_engine::l_state_set_value(device_state_entry *d, UINT64 val) { device_state_interface *state = d->parent_state(); if(state) { state->set_state_int(d->index(), val); luaThis->machine().save().dispatch_presave(); } } //------------------------------------------------- // mem_read - templated memory readers for , // -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000) //------------------------------------------------- template int lua_engine::lua_addr_space::l_mem_read(lua_State *L) { address_space &sp = luabridge::Stack::get(L, 1); luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); offs_t address = lua_tounsigned(L, 2); T mem_content = 0; switch(sizeof(mem_content) * 8) { case 8: mem_content = sp.read_byte(address); break; case 16: if ((address & 1) == 0) { mem_content = sp.read_word(address); } else { mem_content = sp.read_word_unaligned(address); } break; case 32: if ((address & 3) == 0) { mem_content = sp.read_dword(address); } else { mem_content = sp.read_dword_unaligned(address); } break; case 64: if ((address & 7) == 0) { mem_content = sp.read_qword(address); } else { mem_content = sp.read_qword_unaligned(address); } break; default: break; } if (std::numeric_limits::is_signed) { lua_pushinteger(L, mem_content); } else { lua_pushunsigned(L, mem_content); } return 1; } //------------------------------------------------- // mem_write - templated memory writer for , // -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D) //------------------------------------------------- template int lua_engine::lua_addr_space::l_mem_write(lua_State *L) { address_space &sp = luabridge::Stack::get(L, 1); luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected"); offs_t address = lua_tounsigned(L, 2); T val = lua_tounsigned(L, 3); switch(sizeof(val) * 8) { case 8: sp.write_byte(address, val); break; case 16: if ((address & 1) == 0) { sp.write_word(address, val); } else { sp.read_word_unaligned(address, val); } break; case 32: if ((address & 3) == 0) { sp.write_dword(address, val); } else { sp.write_dword_unaligned(address, val); } break; case 64: if ((address & 7) == 0) { sp.write_qword(address, val); } else { sp.write_qword_unaligned(address, val); } break; default: break; } return 0; } //------------------------------------------------- // screen_height - return screen visible height // -> manager:machine().screens[":screen"]:height() //------------------------------------------------- int lua_engine::lua_screen::l_height(lua_State *L) { screen_device *sc = luabridge::Stack::get(L, 1); if(!sc) { return 0; } lua_pushunsigned(L, sc->visible_area().height()); return 1; } //------------------------------------------------- // screen_width - return screen visible width // -> manager:machine().screens[":screen"]:width() //------------------------------------------------- int lua_engine::lua_screen::l_width(lua_State *L) { screen_device *sc = luabridge::Stack::get(L, 1); if(!sc) { return 0; } lua_pushunsigned(L, sc->visible_area().width()); return 1; } //------------------------------------------------- // draw_box - draw a box on a screen container // -> manager:machine().screens[":screen"]:draw_box(x1, y1, x2, y2, bgcolor, linecolor) //------------------------------------------------- int lua_engine::lua_screen::l_draw_box(lua_State *L) { screen_device *sc = luabridge::Stack::get(L, 1); if(!sc) { return 0; } // ensure that we got 6 numerical parameters luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 6), 6, "background color (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 7), 7, "outline color (integer) expected"); // retrieve all parameters float x1, y1, x2, y2; x1 = MIN(lua_tounsigned(L, 2) / static_cast(sc->visible_area().width()) , 1.0f); y1 = MIN(lua_tounsigned(L, 3) / static_cast(sc->visible_area().height()), 1.0f); x2 = MIN(lua_tounsigned(L, 4) / static_cast(sc->visible_area().width()) , 1.0f); y2 = MIN(lua_tounsigned(L, 5) / static_cast(sc->visible_area().height()), 1.0f); UINT32 bgcolor = lua_tounsigned(L, 6); UINT32 fgcolor = lua_tounsigned(L, 7); // draw the box render_container &rc = sc->container(); ui_manager &ui = sc->machine().ui(); ui.draw_outlined_box(&rc, x1, y1, x2, y2, fgcolor, bgcolor); return 0; } //------------------------------------------------- // draw_line - draw a line on a screen container // -> manager:machine().screens[":screen"]:draw_line(x1, y1, x2, y2, linecolor) //------------------------------------------------- int lua_engine::lua_screen::l_draw_line(lua_State *L) { screen_device *sc = luabridge::Stack::get(L, 1); if(!sc) { return 0; } // ensure that we got 5 numerical parameters luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 6), 6, "color (integer) expected"); // retrieve all parameters float x1, y1, x2, y2; x1 = MIN(lua_tounsigned(L, 2) / static_cast(sc->visible_area().width()) , 1.0f); y1 = MIN(lua_tounsigned(L, 3) / static_cast(sc->visible_area().height()), 1.0f); x2 = MIN(lua_tounsigned(L, 4) / static_cast(sc->visible_area().width()) , 1.0f); y2 = MIN(lua_tounsigned(L, 5) / static_cast(sc->visible_area().height()), 1.0f); UINT32 color = lua_tounsigned(L, 6); // draw the line sc->container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); return 0; } //------------------------------------------------- // draw_text - draw text on a screen container // -> manager:machine().screens[":screen"]:draw_text(x, y, message) //------------------------------------------------- int lua_engine::lua_screen::l_draw_text(lua_State *L) { screen_device *sc = luabridge::Stack::get(L, 1); if(!sc) { return 0; } // ensure that we got proper parameters luaL_argcheck(L, lua_isnumber(L, 2), 2, "x (integer) expected"); luaL_argcheck(L, lua_isnumber(L, 3), 3, "y (integer) expected"); luaL_argcheck(L, lua_isstring(L, 4), 4, "message (string) expected"); // retrieve all parameters float x = MIN(lua_tounsigned(L, 2) / static_cast(sc->visible_area().width()) , 1.0f); float y = MIN(lua_tounsigned(L, 3) / static_cast(sc->visible_area().height()), 1.0f); const char *msg = luaL_checkstring(L,4); // TODO: add optional parameters (colors, etc.) // draw the text render_container &rc = sc->container(); ui_manager &ui = sc->machine().ui(); ui.draw_text_full(&rc, msg, x, y , (1.0f - x), JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL); 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(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(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)); luaThis->machine().schedule_hard_reset(); } 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; int ready; astring response; int status; int done; } msg; osd_lock *lock; void lua_engine::serve_lua() { osd_sleep(osd_ticks_per_second() / 1000 * 50); printf("%s v%s - %s\n%s\n%s\n\n", emulator_info::get_applongname(),build_version,emulator_info::get_fulllongname(),emulator_info::get_copyright_info(),LUA_COPYRIGHT); fflush(stdout); char buff[LUA_MAXINPUT]; astring oldbuff; const char *b = LUA_PROMPT; do { // Wait for input fputs(b, stdout); fflush(stdout); /* show prompt */ fgets(buff, LUA_MAXINPUT, stdin); // Create message osd_lock_acquire(lock); if (msg.ready == 0) { msg.text = oldbuff; if (oldbuff.len()!=0) msg.text.cat("\n"); msg.text.cat(buff); msg.ready = 1; msg.done = 0; } osd_lock_release(lock); // Wait for response int done = 0; do { osd_sleep(osd_ticks_per_second() / 1000); osd_lock_acquire(lock); done = msg.done; osd_lock_release(lock); } while (done==0); // Do action on client side osd_lock_acquire(lock); if (msg.status == -1){ b = LUA_PROMPT2; oldbuff = msg.response; } else { b = LUA_PROMPT; oldbuff = ""; } msg.done = 0; osd_lock_release(lock); } while (1); } static void *serve_lua(void *param) { lua_engine *engine = (lua_engine *)param; engine->serve_lua(); return NULL; } //------------------------------------------------- // lua_engine - constructor //------------------------------------------------- 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); luaopen_ioport(m_lua_state); lua_gc(m_lua_state, LUA_GCRESTART, 0); msg.ready = 0; msg.status = 0; msg.done = 0; lock = osd_lock_alloc(); } //------------------------------------------------- // ~lua_engine - destructor //------------------------------------------------- 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 //------------------------------------------------- void lua_engine::initialize() { luabridge::getGlobalNamespace (m_lua_state) .beginNamespace ("emu") .addCFunction ("app_name", l_emu_app_name ) .addCFunction ("app_version", l_emu_app_version ) .addCFunction ("gamename", l_emu_gamename ) .addCFunction ("romname", l_emu_romname ) .addCFunction ("keypost", l_emu_keypost ) .addCFunction ("hook_output", l_emu_hook_output ) .addCFunction ("sethook", l_emu_set_hook ) .addCFunction ("time", l_emu_time ) .addCFunction ("wait", l_emu_wait ) .addCFunction ("after", l_emu_after ) .addCFunction ("exit", l_emu_exit ) .addCFunction ("start", l_emu_start ) .addCFunction ("pause", l_emu_pause ) .addCFunction ("unpause", l_emu_unpause ) .beginClass ("manager") .addFunction ("machine", &machine_manager::machine) .addFunction ("options", &machine_manager::options) .endClass () .beginClass ("machine") .addFunction ("exit", &running_machine::schedule_exit) .addFunction ("hard_reset", &running_machine::schedule_hard_reset) .addFunction ("soft_reset", &running_machine::schedule_soft_reset) .addFunction ("system", &running_machine::system) .addProperty ("devices", &lua_engine::l_machine_get_devices) .addProperty ("screens", &lua_engine::l_machine_get_screens) .endClass () .beginClass ("game_driver") .addData ("name", &game_driver::name) .addData ("description", &game_driver::description) .addData ("year", &game_driver::year) .addData ("manufacturer", &game_driver::manufacturer) .endClass () .beginClass ("device") .addFunction ("name", &device_t::name) .addFunction ("shortname", &device_t::shortname) .addFunction ("tag", &device_t::tag) .addProperty ("spaces", &lua_engine::l_dev_get_memspaces) .addProperty ("state", &lua_engine::l_dev_get_states) .endClass() .beginClass ("lua_addr_space") .addCFunction ("read_i8", &lua_addr_space::l_mem_read) .addCFunction ("read_u8", &lua_addr_space::l_mem_read) .addCFunction ("read_i16", &lua_addr_space::l_mem_read) .addCFunction ("read_u16", &lua_addr_space::l_mem_read) .addCFunction ("read_i32", &lua_addr_space::l_mem_read) .addCFunction ("read_u32", &lua_addr_space::l_mem_read) .addCFunction ("read_i64", &lua_addr_space::l_mem_read) .addCFunction ("read_u64", &lua_addr_space::l_mem_read) .addCFunction ("write_i8", &lua_addr_space::l_mem_write) .addCFunction ("write_u8", &lua_addr_space::l_mem_write) .addCFunction ("write_i16", &lua_addr_space::l_mem_write) .addCFunction ("write_u16", &lua_addr_space::l_mem_write) .addCFunction ("write_i32", &lua_addr_space::l_mem_write) .addCFunction ("write_u32", &lua_addr_space::l_mem_write) .addCFunction ("write_i64", &lua_addr_space::l_mem_write) .addCFunction ("write_u64", &lua_addr_space::l_mem_write) .endClass() .deriveClass ("addr_space") .addFunction("name", &address_space::name) .endClass() .beginClass ("lua_screen_dev") .addCFunction ("draw_box", &lua_screen::l_draw_box) .addCFunction ("draw_line", &lua_screen::l_draw_line) .addCFunction ("draw_text", &lua_screen::l_draw_text) .addCFunction ("height", &lua_screen::l_height) .addCFunction ("width", &lua_screen::l_width) .endClass() .deriveClass ("screen_dev") .addFunction ("frame_number", &screen_device::frame_number) .addFunction ("name", &screen_device::name) .addFunction ("shortname", &screen_device::shortname) .addFunction ("tag", &screen_device::tag) .endClass() .beginClass ("dev_space") .addFunction ("name", &device_state_entry::symbol) .addProperty ("value", &lua_engine::l_state_get_value, &lua_engine::l_state_set_value) .addFunction ("is_visible", &device_state_entry::visible) .addFunction ("is_divider", &device_state_entry::divider) .endClass() .endNamespace(); luabridge::push (m_lua_state, machine_manager::instance()); lua_setglobal(m_lua_state, "manager"); } void lua_engine::start_console() { mg_start_thread(::serve_lua, this); } //------------------------------------------------- // frame_hook - called at each frame refresh, used to draw a HUD //------------------------------------------------- bool lua_engine::frame_hook() { bool is_cb_hooked = false; if (m_machine != NULL) { // invoke registered callback (if any) is_cb_hooked = hook_frame_cb.active(); if (is_cb_hooked) { lua_State *L = hook_frame_cb.precall(); hook_frame_cb.call(this, L, 0); } } return is_cb_hooked; } void lua_engine::periodic_check() { osd_lock_acquire(lock); if (msg.ready == 1) { lua_settop(m_lua_state, 0); int status = luaL_loadbuffer(m_lua_state, msg.text.cstr(), strlen(msg.text.cstr()), "=stdin"); if (incomplete(status)==0) /* cannot try to add lines? */ { if (status == LUA_OK) status = docall(0, LUA_MULTRET); report(status); if (status == LUA_OK && lua_gettop(m_lua_state) > 0) /* any result to print? */ { luaL_checkstack(m_lua_state, LUA_MINSTACK, "too many results to print"); lua_getglobal(m_lua_state, "print"); lua_insert(m_lua_state, 1); if (lua_pcall(m_lua_state, lua_gettop(m_lua_state) - 1, 0, 0) != LUA_OK) lua_writestringerror("%s\n", lua_pushfstring(m_lua_state, "error calling " LUA_QL("print") " (%s)", lua_tostring(m_lua_state, -1))); } } else { status = -1; } msg.status = status; msg.response = msg.text; msg.text = ""; msg.ready = 0; msg.done = 1; } osd_lock_release(lock); } //------------------------------------------------- // close - close and cleanup of lua engine //------------------------------------------------- void lua_engine::close() { lua_settop(m_lua_state, 0); /* clear stack */ lua_close(m_lua_state); } //------------------------------------------------- // execute - load and execute script //------------------------------------------------- void lua_engine::load_script(const char *filename) { int s = luaL_loadfile(m_lua_state, filename); report(s); update_machine(); start(); } //------------------------------------------------- // execute_string - execute script from string //------------------------------------------------- void lua_engine::load_string(const char *value) { int s = luaL_loadstring(m_lua_state, value); report(s); update_machine(); start(); } //------------------------------------------------- // start - execute the loaded script //------------------------------------------------- void lua_engine::start() { resume(m_lua_state); } //************************************************************************** // LuaBridge Stack specializations //************************************************************************** namespace luabridge { template <> struct Stack { static inline void push (lua_State* L, UINT64 value) { lua_pushunsigned(L, static_cast (value)); } static inline UINT64 get (lua_State* L, int index) { return static_cast (luaL_checkunsigned (L, index)); } }; }