summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/luaengine.cpp
blob: 989cb78f72b7ad9b22fda3d82694a5cbb761298b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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: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));
		}
	};
}