// license:BSD-3-Clause
// copyright-holders:David Haywood
/* Early Maygay HW
I believe this is 'Triple M' or 'MMM' hardware
Z80 based Fruit Machine
*/
#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "sound/ay8910.h"
#include "speaker.h"
class mmm_state : public driver_device
{
public:
mmm_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_ctc(*this, "ctc"),
m_inputs(*this, "IN%u", 0)
{ }
DECLARE_WRITE8_MEMBER(strobe_w);
DECLARE_READ8_MEMBER(inputs_r);
DECLARE_WRITE8_MEMBER(ay_porta_w);
void mmm(machine_config &config);
void io_map(address_map &map);
void mem_map(address_map &map);
private:
required_device<cpu_device> m_maincpu;
required_device<z80ctc_device> m_ctc;
required_ioport_array<8> m_inputs;
u8 m_strobe;
};
WRITE8_MEMBER(mmm_state::strobe_w)
{
m_strobe = data;
}
READ8_MEMBER(mmm_state::inputs_r)
{
u8 result = 0xff;
for (int i = 0; i < 8; i++)
if (BIT(m_strobe, i))
result &= m_inputs[i]->read();
return result;
}
WRITE8_MEMBER(mmm_state::ay_porta_w)
{
logerror("Writing %02X to AY-3-8910 port A\n", data);
}
void mmm_state::mem_map(address_map &map)
{
map(0x0000, 0x3fff).rom();
map(0x4000, 0x40ff).ram();
}
void mmm_state::io_map(address_map &map)
{
map.global_mask(0xff);
map(0x00, 0x00).w(this, FUNC(mmm_state::strobe_w));
map(0x03, 0x03).w("aysnd", FUNC(ay8910_device::address_w));
map(0x04, 0x04).w("aysnd", FUNC(ay8910_device::data_w));
map(0x05, 0x05).r("aysnd", FUNC(ay8910_device::data_r));
map(0x06, 0x06).select(0x30).lrw8("ctc_rw",
[this](address_space &space, offs_t offset, u8 mem_mask) {
return m_ctc->read(space, offset >> 4, mem_mask);
},
[this](address_space &space, offs_t offset, u8 data, u8 mem_mask) {
m_ctc->write(space, offset >> 4, data, mem_mask);
});
map(0x07, 0x07).r(this, FUNC(mmm_state::inputs_r));
}
static INPUT_PORTS_START( mmm )
PORT_START("IN0")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_START("IN1")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_START("IN2")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_START("IN3")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_START("IN4")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
PORT_START("IN5")
PORT_BIT( pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888 } /* Comment */
.highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
.highlight .k { color: #080; font-weight: bold } /* Keyword */
.highlight .ch { color: #888 } /* Comment.Hashbang */
.highlight .cm { color: #888 } /* Comment.Multiline */
.highlight .cp { color: #C00; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888 } /* Comment.Single */
.highlight .cs { color: #C00; font-weight: bold; background-color: #FFF0F0 } /* Comment.Special */
.highlight .gd { color: #000; background-color: #FDD } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #A00 } /* Generic.Error */
.highlight .gh { color: #333 } /* Generic.Heading */
.highlight .gi { color: #000; background-color: #DFD } /* Generic.Inserted */
.highlight .go { color: #888 } /* Generic.Output */
.highlight .gp { color: #555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666 } /* Generic.Subheading */
.highlight .gt { color: #A00 } /* Generic.Traceback */
.highlight .kc { color: #080; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #080; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #080; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #080 } /* Keyword.Pseudo */
.highlight .kr { color: #080; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #00D; font-weight: bold } /* Literal.Number */
.highlight .s { color: #D20; background-color: #FFF0F0 } /* Literal.String */
.highlight .na { color: #369 } /* Name.Attribute */
.highlight .nb { color: #038 } /* Name.Builtin */
.highlight .nc { color: #B06; font-weight: bold } /* Name.Class */
.highlight .no { color: #036; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555 } /* Name.Decorator */
.highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #06B; font-weight: bold } /* Name.Function */
.highlight .nl { color: #369; font-style: italic } /* Name.Label */
.highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #369; font-weight: bold } /* Name.Property */
.highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #369 } /* Name.Variable */
.highlight .ow { color: #080 } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */
.highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */
.highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */
.highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */
.highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */
.highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */
.highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */
.highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */
.highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */
.highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */
.highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */
.highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */
.highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */
.highlight .bp { color: #038 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #369 } /* Name.Variable.Class */
.highlight .vg { color: #D70 } /* Name.Variable.Global */
.highlight .vi { color: #33B } /* Name.Variable.Instance */
.highlight .vm { color: #369 } /* Name.Variable.Magic */
.highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,Luca Bruno
/***************************************************************************
luaengine.c
Controls execution of the core MAME system.
***************************************************************************/
#include <limits>
#include "lua.hpp"
#include "luabridge/Source/LuaBridge/LuaBridge.h"
#include <signal.h>
#include "emu.h"
#include "emuopts.h"
#include "osdepend.h"
#include "drivenum.h"
#include "ui/ui.h"
#include "luaengine.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 "<eof>"
#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;
}
#if (defined(__sun__) && defined(__svr4__)) || defined(__ANDROID__) || defined(__OpenBSD__)
#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<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_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<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;
}
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<screen_device *>(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<running_machine *>(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<device_t *>(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<device_t *>(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<device_state_entry *>(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 <sign>,<size>
// -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000)
//-------------------------------------------------
template <typename T>
int lua_engine::lua_addr_space::l_mem_read(lua_State *L)
{
address_space &sp = luabridge::Stack<address_space &>::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<T>::is_signed) {
lua_pushinteger(L, mem_content);
} else {
lua_pushunsigned(L, mem_content);
}
return 1;
}
//-------------------------------------------------
// mem_write - templated memory writer for <sign>,<size>
// -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D)
//-------------------------------------------------
template <typename T>
int lua_engine::lua_addr_space::l_mem_write(lua_State *L)
{
address_space &sp = luabridge::Stack<address_space &>::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<screen_device *>::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<screen_device *>::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<screen_device *>::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
int sc_width = sc->visible_area().width();
int sc_height = sc->visible_area().height();
float x1, y1, x2, y2;
x1 = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width);
y1 = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height);
x2 = MIN(MAX(0, (float) lua_tonumber(L, 4)), sc_width-1) / static_cast<float>(sc_width);
y2 = MIN(MAX(0, (float) lua_tonumber(L, 5)), sc_height-1) / static_cast<float>(sc_height);
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<screen_device *>::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
int sc_width = sc->visible_area().width();
int sc_height = sc->visible_area().height();
float x1, y1, x2, y2;
x1 = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width);
y1 = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height);
x2 = MIN(MAX(0, (float) lua_tonumber(L, 4)), sc_width-1) / static_cast<float>(sc_width);
y2 = MIN(MAX(0, (float) lua_tonumber(L, 5)), sc_height-1) / static_cast<float>(sc_height);
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<screen_device *>::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");
luaL_argcheck(L, lua_isinteger(L, 5) || lua_isnone(L, 5), 5, "optional argument: text color, integer expected (default: 0xffffffff)");
// retrieve all parameters
int sc_width = sc->visible_area().width();
int sc_height = sc->visible_area().height();
float x = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width);
float y = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height);
const char *msg = luaL_checkstring(L,4);
rgb_t textcolor = UI_TEXT_COLOR;
if (!lua_isnone(L, 5)) {
textcolor = rgb_t(lua_tounsigned(L, 5));
}
// 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, textcolor,
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<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));
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 {
std::string text;
int ready;
std::string 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];
std::string 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.length()!=0) msg.text.append("\n");
msg.text.append(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 <machine_manager> ("manager")
.addFunction ("machine", &machine_manager::machine)
.addFunction ("options", &machine_manager::options)
.endClass ()
.beginClass <running_machine> ("machine")
.addFunction ("exit", &running_machine::schedule_exit)
.addFunction ("hard_reset", &running_machine::schedule_hard_reset)
.addFunction ("soft_reset", &running_machine::schedule_soft_reset)
.addFunction ("save", &running_machine::schedule_save)
.addFunction ("load", &running_machine::schedule_load)
.addFunction ("system", &running_machine::system)
.addProperty <luabridge::LuaRef, void> ("devices", &lua_engine::l_machine_get_devices)
.addProperty <luabridge::LuaRef, void> ("screens", &lua_engine::l_machine_get_screens)
.endClass ()
.beginClass <game_driver> ("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_t> ("device")
.addFunction ("name", &device_t::name)
.addFunction ("shortname", &device_t::shortname)
.addFunction ("tag", &device_t::tag)
.addProperty <luabridge::LuaRef, void> ("spaces", &lua_engine::l_dev_get_memspaces)
.addProperty <luabridge::LuaRef, void> ("state", &lua_engine::l_dev_get_states)
.endClass()
.beginClass <lua_addr_space> ("lua_addr_space")
.addCFunction ("read_i8", &lua_addr_space::l_mem_read<INT8>)
.addCFunction ("read_u8", &lua_addr_space::l_mem_read<UINT8>)
.addCFunction ("read_i16", &lua_addr_space::l_mem_read<INT16>)
.addCFunction ("read_u16", &lua_addr_space::l_mem_read<UINT16>)
.addCFunction ("read_i32", &lua_addr_space::l_mem_read<INT32>)
.addCFunction ("read_u32", &lua_addr_space::l_mem_read<UINT32>)
.addCFunction ("read_i64", &lua_addr_space::l_mem_read<INT64>)
.addCFunction ("read_u64", &lua_addr_space::l_mem_read<UINT64>)
.addCFunction ("write_i8", &lua_addr_space::l_mem_write<INT8>)
.addCFunction ("write_u8", &lua_addr_space::l_mem_write<UINT8>)
.addCFunction ("write_i16", &lua_addr_space::l_mem_write<INT16>)
.addCFunction ("write_u16", &lua_addr_space::l_mem_write<UINT16>)
.addCFunction ("write_i32", &lua_addr_space::l_mem_write<INT32>)
.addCFunction ("write_u32", &lua_addr_space::l_mem_write<UINT32>)
.addCFunction ("write_i64", &lua_addr_space::l_mem_write<INT64>)
.addCFunction ("write_u64", &lua_addr_space::l_mem_write<UINT64>)
.endClass()
.deriveClass <address_space, lua_addr_space> ("addr_space")
.addFunction("name", &address_space::name)
.endClass()
.beginClass <lua_screen> ("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_device, lua_screen> ("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 <device_state_entry> ("dev_space")
.addFunction ("name", &device_state_entry::symbol)
.addProperty <UINT64, UINT64> ("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()
{
}
//-------------------------------------------------
// 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.c_str(), strlen(msg.text.c_str()), "=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 <UINT64> {
static inline void push (lua_State* L, UINT64 value) {
lua_pushunsigned(L, static_cast <lua_Unsigned> (value));
}
static inline UINT64 get (lua_State* L, int index) {
return static_cast <UINT64> (luaL_checkunsigned (L, index));
}
};
}