summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/jpms80.c
blob: 3091f14f01c0b588777f7743b3f97d5585beddce (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/* JPM System 80 Hardware

  TMS9995 CPU

  +

  ???

  AY8910?

  TMS9902
  (is there a 9901 as well?)

 ---

 There are also older platforms also using the TMS CPU, we load some of those roms here too, but many should go into the SRU folder.


System80 is based on the SRU platform, but with more outputs and finally a separate CPU and sound board setup to permit easy repair.
 
*/


#include "emu.h"
#include "cpu/tms9900/tms9900l.h"
#include "sound/ay8910.h"
#include "machine/tms9902.h"

class jpms80_state : public driver_device
{
public:
	jpms80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
			m_maincpu(*this, "maincpu")
	{ }

protected:

	// devices
	required_device<cpu_device> m_maincpu;
public:
	DECLARE_DRIVER_INIT(jpms80);
};

static ADDRESS_MAP_START( jpms80_map, AS_PROGRAM, 8, jpms80_state )
	AM_RANGE(0x0000, 0x2fff) AM_ROM
	AM_RANGE(0x3000, 0x3fff) AM_RAM
ADDRESS_MAP_END

static ADDRESS_MAP_START( jpms80_io_map, AS_IO, 8, jpms80_state )
	ADDRESS_MAP_GLOBAL_MASK(0x1ff)
//  AM_RANGE(0x0000, 0x000f) // I/O & Optic (in) / Reels (out)
//  AM_RANGE(0x0050, 0x0050) // INT1 enable (lv3)
//  AM_RANGE(0x0051, 0x0051) // INT2 enable (lv4)
//  AM_RANGE(0x0052, 0x0052) // Watchdog
//  AM_RANGE(0x0053, 0x0053) // I/O Enable
//  AM_RANGE(0x0140, 0x015f) // AY
	AM_RANGE(0x01e0, 0x01ff) AM_DEVREADWRITE("tms9902duart", tms9902_device, cruread, cruwrite)
//  Lamps, Meters etc. can move around
ADDRESS_MAP_END


static INPUT_PORTS_START( jpms80 )
INPUT_PORTS_END


static const ay8910_interface ay8910_interface_jpm =
{
	AY8910_LEGACY_OUTPUT,
	AY8910_DEFAULT_LOADS,
	DEVCB_NULL,
	DEVCB_NULL,
	DEVCB_NULL,
	DEVCB_NULL
};


// these are wrong
#define MAIN_CLOCK 2000000
#define SOUND_CLOCK 2000000
#define DUART_CLOCK 2000000

static const tms9902_interface tms9902_config =
{
	DEVCB_NULL,             /*int_callback,*/   /* called when interrupt pin state changes */
	DEVCB_NULL,             /*rcv_callback,*/   /* called when a character shall be received  */
	DEVCB_NULL,             /* called when a character is transmitted */
	DEVCB_NULL              /* called for setting interface parameters and line states */
};


static MACHINE_CONFIG_START( jpms80, jpms80_state )
	/* basic machine hardware */
	MCFG_CPU_ADD("maincpu", TMS9995L, MAIN_CLOCK)
	MCFG_CPU_PROGRAM_MAP(jpms80_map)
	MCFG_CPU_IO_MAP(jpms80_io_map)

	MCFG_SPEAKER_STANDARD_MONO("mono")

	MCFG_TMS9902_ADD("tms9902duart", tms9902_config,    DUART_CLOCK)

	MCFG_SOUND_ADD("aysnd", AY8910, 2000000)
	MCFG_SOUND_CONFIG(ay8910_interface_jpm)
	MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
MACHINE_CONFIG_END

DRIVER_INIT_MEMBER(jpms80_state,jpms80)
{
}



ROM_START( j80bac )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "bankacoinp1.bin", 0x0000, 0x1000, CRC(7b82025b) SHA1(f698688c55f8c5dc891e470de8df2eb12f6b1ec5) )
	ROM_LOAD( "bankacoinp2.bin", 0x1000, 0x1000, CRC(91d71fbe) SHA1(d0c45218b7568d5293f015334d7d1045bcb2fe03) )
	ROM_LOAD( "bankacoinp3.bin", 0x2000, 0x1000, CRC(0c3b2954) SHA1(4342a2a047496caf8569d4519dd8daad47e634e3) )
ROM_END

ROM_START( j80blbnk )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "blankity-bank-p1.bin", 0x0000, 0x1000, CRC(8b2aeca6) SHA1(207db63d5130cb7a8eb9be41d116432f7b7728b0) )
	ROM_LOAD( "blankity-bank-p2.bin", 0x1000, 0x1000, CRC(561262da) SHA1(d1f3e7815c5ea3ba7c26ed3b95a16c802f6af50f) )
	ROM_LOAD( "blankity-bank-p3.bin", 0x2000, 0x1000, CRC(39925035) SHA1(0af8c8f8074c4873581250474d59d715178cded4) )
ROM_END


ROM_START( j80bounc )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "bouncer.p1", 0x0000, 0x1000, CRC(81de115b) SHA1(0890de1492859c58411fd130ecf721df7611247a) )
	ROM_LOAD( "bouncer.p2", 0x1000, 0x1000, CRC(8507ea42) SHA1(e4838fe737c8a9964e0067be460e8bfc18b0a406) )
ROM_END



ROM_START( j80frogh )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "froghop1.bin", 0x0000, 0x1000, CRC(606846f8) SHA1(8796fb647a41dad087b9eb3e24fa7071c933d1ec) )
	ROM_LOAD( "froghop2.bin", 0x1000, 0x1000, CRC(b64ed5ad) SHA1(5697b0a16191ee3845f0f4077cf7b597f0b20024) )
	ROM_LOAD( "froghop3.bin", 0x2000, 0x1000, CRC(f5b55c0e) SHA1(9fdef9f634f9b832a1bf6e3e3890a7fa328d20e3) )
ROM_END



ROM_START( j80fruit )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "fruit_snappa_1-1.bin", 0x0000, 0x1000, CRC(f6eea72d) SHA1(ae994f9eb68aa6ea127586afb448cc8fbff0c314) )
	ROM_LOAD( "fruit_snappa_1-2.bin", 0x1000, 0x1000, CRC(10eccac5) SHA1(3c9cc57a3b51fdae713c11a33677555be3f669bc) )
	ROM_LOAD( "fruit_snappa_1-3.bin", 0x2000, 0x1000, CRC(6f938a9a) SHA1(edbf44ae7cb060420b6f952652f08271c4af35bd) )

	ROM_REGION( 0x10000, "altrevs", 0 )
	// from 'fruit chaser' set, other roms matched.
	ROM_LOAD( "fruit31.p1", 0x0000, 0x001000, CRC(406ff4c1) SHA1(10ef59e66debb15b22d25b66ed19f45a242e30ac) )
ROM_END



ROM_START( j80golds )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "goldstep2-1.p1", 0x0000, 0x1000, CRC(bc1e0788) SHA1(5e01881bda22fc00b2d2ac2b80acc67caddea682) )
	ROM_LOAD( "goldstep2-2.p2", 0x1000, 0x1000, CRC(6ea82bd9) SHA1(289c9a076b9e5039f09283d64ceb77dfd7ea79ea) )
ROM_END



ROM_START( j80hotln )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "lines2_1.rom", 0x0000, 0x1000, CRC(f0ce5d7f) SHA1(be3f8ff3f83737a004d6a78cc61c3385307df1c3) )
	ROM_LOAD( "lines2_2.rom", 0x1000, 0x1000, CRC(d5e69b49) SHA1(fcaa527875f81e03c5a5866d6d8b017450c50d9c) )
ROM_END



ROM_START( j80myspn )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ms1.bin", 0x0000, 0x1000, CRC(b247374e) SHA1(33399f39bba68eff13e05529174d17f5b1ca0f70) )
	ROM_LOAD( "ms2.bin", 0x1000, 0x1000, CRC(721c35df) SHA1(05ea0cdc83823f268becc7b9dd99db61949ad229) )
ROM_END



ROM_START( j80nudg2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ndu.p1", 0x0000, 0x1000, CRC(4cfd3c6f) SHA1(06ad825343178a694585ee3b4ff8400caf15dd21) ) // aka ndudx5-1-5p.p1 (was in j2nuddud)
ROM_END



ROM_START( j80rr )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "jpmroadrunnerp1.bin", 0x0000, 0x1000, CRC(86f50997) SHA1(8bb266274d3ebeee942e5f878f7faae012712382) )
	ROM_LOAD( "jpmroadrunnerp2.bin", 0x1000, 0x1000, CRC(aea12b9e) SHA1(6f6eb286c43a9bc04bfcab71713ce59da61cc063) )
	ROM_LOAD( "jpmroadrunnerp3.bin", 0x2000, 0x1000, CRC(9b0b6fb9) SHA1(0282189e2945e4aa3a338930666d1eb34022894c) )
ROM_END


ROM_START( j80rra )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "rr.p1", 0x0000, 0x1000, CRC(38cd5043) SHA1(f4b828ad2e761bba91336714357a18f10d79c22b) )
	ROM_LOAD( "rr.p2", 0x1000, 0x1000, CRC(81dc46ec) SHA1(17c60590cf5628df6bd109213a3f671b1a6df14b) )
	ROM_LOAD( "rr.p3", 0x2000, 0x1000, CRC(5e617600) SHA1(1a2a25f81818fc3abeceb74608b2ffd53fac2c6d) )
ROM_END






ROM_START( j80supst )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "supasteppa2-1.p1", 0x0000, 0x1000, CRC(aac5b165) SHA1(5bf4acb85be227e1f4979fea4552fa5f64e9b7b2) )
	ROM_LOAD( "supasteppa2-2.p2", 0x1000, 0x1000, CRC(3a93ea9e) SHA1(24e711a398d7f071fb904993ff0a974b4ac8b1d6) )
ROM_END



ROM_START( j80supbk )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "sbank-4.1.bin", 0x0000, 0x1000, CRC(effd29fa) SHA1(1e20bc6130f5d49db3856c56c64746f3fa49bd9c) )
	ROM_LOAD( "sbank-4.2.bin", 0x1000, 0x1000, CRC(6ca5cc1d) SHA1(77d9bb44e6837027b61286f30bcb2c1b0e6a53fb) )
	ROM_LOAD( "sbank-4.3.bin", 0x2000, 0x1000, CRC(af08594d) SHA1(ebff60e63e99af102874f4b3f070d9bfd229ab89) )
ROM_END



ROM_START( j80topsp )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "topsprint4-1.p1", 0x0000, 0x1000, CRC(91c4f494) SHA1(e4fd688a1fd23694c4fe8529d07ac248f262ad70) )
	ROM_LOAD( "topsprint4-2.p2", 0x1000, 0x1000, CRC(e9ad3706) SHA1(bb6cb1a8ea740be017055e4fa621fabc8df77086) )
	ROM_LOAD( "topsprint4-3.p3", 0x2000, 0x1000, CRC(d1abfb54) SHA1(33b11563c6e1ddfaa5527ad7a384fecd03c7de0a) )
ROM_END



ROM_START( j80topup )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "topup3-1.bin", 0x0000, 0x1000, CRC(2feb37e8) SHA1(098671f81fa94b851a8fa41ee7bd3d1b762eb824) )
	ROM_LOAD( "topup3-2.bin", 0x1000, 0x1000, CRC(1937e7c9) SHA1(a9ae5163e560642598ec9878276d8785c28eb035) )
	ROM_LOAD( "topup3-3.bin", 0x2000, 0x1000, CRC(283d7dd2) SHA1(8246c80c85956a0a3b59d68700319a59b35a5326) )
ROM_END



ROM_START( j80tumbl )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "tumble3-1.bin", 0x0000, 0x1000, CRC(2feb37e8) SHA1(098671f81fa94b851a8fa41ee7bd3d1b762eb824) )
	ROM_LOAD( "tumble3-2.bin", 0x1000, 0x1000, CRC(1937e7c9) SHA1(a9ae5163e560642598ec9878276d8785c28eb035) )
	ROM_LOAD( "tumble3-3.bin", 0x2000, 0x1000, CRC(23789c80) SHA1(6b6ac4e1dc66d5eb399437e87a9e7ee461bee086) )
ROM_END


ROM_START( j80wsprt )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "winsprint4-1.bin", 0x0000, 0x1000, CRC(57259716) SHA1(76ac953f3edefd0fe540e931bd73f564ea49d0c5) )
	ROM_LOAD( "winsprint4-2.bin", 0x1000, 0x1000, CRC(f097bde3) SHA1(22bccd74564fbc2df63ffed3972199d25059b881) )
	ROM_LOAD( "winsprint4-3.bin", 0x2000, 0x1000, CRC(c7318871) SHA1(6534f06be122ed0c9665437485fb860547dd1d7f) )
ROM_END

ROM_START( j80wsprt3 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "winsprint3-1.bin", 0x0000, 0x1000, CRC(1af581c2) SHA1(5e04bf37551fc8fc7f477770635f8a573d659a2f) )
	ROM_LOAD( "winsprint3-2.bin", 0x1000, 0x1000, CRC(f097bde3) SHA1(22bccd74564fbc2df63ffed3972199d25059b881) )
	ROM_LOAD( "winsprint3-3.bin", 0x2000, 0x1000, CRC(73098120) SHA1(e353714350681c84f4d3d093dbf07f7e8c70e109) )
ROM_END

ROM_START( j80wsprt2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "winsprint.p1", 0x0000, 0x1000, CRC(e440c7bb) SHA1(5ef85a93a6170115c750257ac6c755b18b3114a9) )
	ROM_LOAD( "winsprint.p2", 0x1000, 0x1000, CRC(225674bf) SHA1(d8a15226ff4f7b16f7f1a8dff969585a6b4536fe) )
	ROM_LOAD( "winsprint.p3", 0x2000, 0x1000, CRC(51d11f59) SHA1(756ba5f02c0733d082767cbdaa93105a7d3f31f3) )
ROM_END

ROM_START( j80alad )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "al_p1.bin", 0x0000, 0x001000, CRC(41134b85) SHA1(8d48af9e3eae8dcc5888a3fa8ae9681ff6047dbb) )
	ROM_LOAD( "al_p2.bin", 0x1000, 0x001000, CRC(934248eb) SHA1(2abb23907acf5036b63185afb164117f1d1bab0c) )
	ROM_LOAD( "al_p3.bin", 0x2000, 0x001000, CRC(da2a56a3) SHA1(1b125be1bd4f0d63c68f370a2a7202f68ff11577) )
ROM_END

ROM_START( j80fortr )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "fortunetrail1.1", 0x0000, 0x001000, CRC(1691b72f) SHA1(be0966c9560249f2529dd76421fe6646adaeeadb) )
	ROM_LOAD( "fortunetrail1.2", 0x1000, 0x001000, CRC(02dfcfc4) SHA1(ae97b670a4d5b341ee150cabf3d264a02cd7a32c) )
ROM_END

ROM_START( j80mster )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "masterspyp1.bin", 0x0000, 0x001000, CRC(7264e304) SHA1(cd50fd116226de2980f31728faa6723dca0ac061) )
	ROM_LOAD( "masterspyp2.bin", 0x1000, 0x001000, CRC(e6349ee9) SHA1(207a4089e7128143eaeebe1dc082b742b5dfab96) )
	ROM_LOAD( "masterspyp3.bin", 0x2000, 0x001000, CRC(83868a58) SHA1(3264dfa7994c62615da2418d1667cb3bd2e00435) )
ROM_END

ROM_START( j80plsnd )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "plus_p1.bin", 0x0000, 0x000400, CRC(02721d4f) SHA1(ea5da3f08098a9d12c71d41d70f09aca6660d6c5) )
	ROM_LOAD( "plus_p2.bin", 0x0400, 0x000400, CRC(f58b492f) SHA1(569805044fa64c1d0c3620f380b4a09152ce2964) )
	ROM_LOAD( "plus_p3.bin", 0x0800, 0x000400, CRC(e9584323) SHA1(7b2101626920bed533b392d1064fde305c8c18e8) )
	ROM_LOAD( "plus_p4.bin", 0x0c00, 0x000400, CRC(67f9d05f) SHA1(1c441c775f2126861858c65c7634773a86f4fcc5) )
	ROM_LOAD( "plus_p5.bin", 0x1000, 0x000400, CRC(d111b2c6) SHA1(c0182a4b163e4dbb67f1c98251b93fa878bff2e2) )
ROM_END



GAME(198?, j80bac   ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Bank A Coin (JPM) (SYSTEM80)",                       GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80bounc ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Bouncer (JPM) (SYSTEM80)",                       GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80frogh ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Frog Hop (JPM) (SYSTEM80)",                      GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80fruit ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Fruit Snappa (JPM) (SYSTEM80)",                      GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80golds ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Golden Steppa (JPM) (SYSTEM80)",                     GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80hotln ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Hot Lines (JPM) (SYSTEM80)",                     GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80myspn ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Mystery Spin (JPM) (SYSTEM80)",                      GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80nudg2 ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Nudge Double Up MkII (JPM) (SYSTEM80)",                      GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80rr    ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Road Runner (JPM) (SYSTEM80, set 1)",                        GAME_IS_SKELETON_MECHANICAL ) // was also in a set named 'Route 66' with identical roms, but text in ROM indicates name is Road Runner, maybe a reskin?
GAME(198?, j80rra   ,j80rr      ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Road Runner (JPM) (SYSTEM80, set 2)",                        GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80supst ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Supa Steppa (JPM) (SYSTEM80)",                       GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80supbk ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Superbank (JPM) (SYSTEM80)",                     GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80topsp ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Top Sprint (JPM) (SYSTEM80)",                        GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80topup ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Top Up (JPM) (SYSTEM80)",                        GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80tumbl ,0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Tumble (JPM) (SYSTEM80)",                        GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80wsprt, 0          ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Winsprint (JPM) (V4, 5x20p) (SYSTEM80)",                     GAME_IS_SKELETON_MECHANICAL )
GAME(198?, j80wsprt3, j80wsprt  ,jpms80,jpms80, jpms80_state,jpms80,ROT0,   "JPM","Winsprint (JPM) (V3, 50p, 5 credits
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,Luca Bruno
/***************************************************************************

    luaengine.c

    Controls execution of the core MAME system.

***************************************************************************/

#include <limits>
#include <thread>
#include <lua.hpp>
#include "luabridge/Source/LuaBridge/LuaBridge.h"
#include <signal.h>
#include "emu.h"
#include "mame.h"
#include "cheat.h"
#include "drivenum.h"
#include "emuopts.h"
#include "ui/ui.h"
#include "luaengine.h"
#include "uiinput.h"
#include <mutex>

#ifdef __clang__
#pragma clang diagnostic ignored "-Wshift-count-overflow"
#endif
//**************************************************************************
//  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) != nullptr)  /* get line */

static lua_State *globalL = nullptr;

#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 = nullptr;

extern "C" {
	int luaopen_zlib(lua_State *L);
	int luaopen_lfs(lua_State *L);
}

static void lstop(lua_State *L, lua_Debug *ar)
{
	(void)ar;  /* unused arg. */
	lua_sethook(L, nullptr, 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 == nullptr) 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;
}

namespace luabridge
{
template <>
struct Stack <osd_file::error>
{
	static void push(lua_State *L, osd_file::error error)
	{
		std::string strerror;
		switch(error)
		{
			case osd_file::error::NONE:
				lua_pushboolean(L, false);
				return;
			case osd_file::error::FAILURE:
				strerror = "failure";
				break;
			case osd_file::error::OUT_OF_MEMORY:
				strerror = "out_of_memory";
				break;
			case osd_file::error::NOT_FOUND:
				strerror = "not_found";
				break;
			case osd_file::error::ACCESS_DENIED:
				strerror = "access_denied";
				break;
			case osd_file::error::ALREADY_OPEN:
				strerror = "already_open";
				break;
			case osd_file::error::TOO_MANY_FILES:
				strerror = "too_many_files";
				break;
			case osd_file::error::INVALID_DATA:
				strerror = "invalid_data";
				break;
			case osd_file::error::INVALID_ACCESS:
				strerror = "invalid_access";
				break;
			default:
				strerror = "unknown_error";
				break;
		}
		lua_pushstring(L, strerror.c_str());
	}
};
template <>
struct Stack <map_handler_type>
{
	static void push(lua_State *L, map_handler_type error)
	{
		std::string type;
		switch(error)
		{
			case AMH_NONE:
				type = "none";
				break;
			case AMH_RAM:
				type = "ram";
				break;
			case AMH_ROM:
				type = "rom";
				break;
			case AMH_NOP:
				type = "nop";
				break;
			case AMH_UNMAP:
				type = "unmap";
				break;
			case AMH_DEVICE_DELEGATE:
				type = "delegate";
				break;
			case AMH_PORT:
				type = "port";
				break;
			case AMH_BANK:
				type = "bank";
				break;
			case AMH_DEVICE_SUBMAP:
				type = "submap";
				break;
			default:
				type = "unknown";
				break;
		}
		lua_pushstring(L, type.c_str());
	}
};
}
/* 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 = nullptr;
	cb = -1;
}

void lua_engine::hook::set(lua_State *lua, int idx)
{
	if (L)
		luaL_unref(L, LUA_REGISTRYINDEX, cb);

	if (lua_isnil(lua, idx)) {
		L = nullptr;
		cb = -1;

	} else {
		L = lua;
		lua_pushvalue(lua, idx);
		cb = luaL_ref(lua, 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, nullptr, 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 *lua, INT32 param)
{
	resume(static_cast<lua_State *>(lua));
}

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_softname - returns softlist name
//-------------------------------------------------

int lua_engine::l_emu_softname(lua_State *L)
{
	lua_pushstring(L, luaThis->machine().options().software_name());
	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, nullptr);
}

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) {
		machine().output().set_notifier(nullptr, 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) {
			machine().output().set_notifier(nullptr, 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");
	}
}

//-------------------------------------------------
//  options_entry - return table of option entries
//  -> manager:options().entries
//  -> manager:machine():options().entries
//  -> manager:machine():ui():options().entries
//-------------------------------------------------

template <typename T>
luabridge::LuaRef lua_engine::l_options_get_entries(const T *o)
{
	T *options = const_cast<T *>(o);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef entries_table = luabridge::LuaRef::newTable(L);

	int unadorned_index = 0;
	for (typename T::entry &curentry : *options)
	{
		const char *name = curentry.name();
		bool is_unadorned = false;
		// check if it's unadorned
		if (name && strlen(name) && !strcmp(name, options->unadorned(unadorned_index)))
		{
			unadorned_index++;
			is_unadorned = true;
		}
		if (!curentry.is_header() && !curentry.is_command() && !curentry.is_internal() && !is_unadorned)
			entries_table[name] = &curentry;
	}

	return entries_table;
}

//-------------------------------------------------
//  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 != nullptr; 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;
}

//-------------------------------------------------
//  machine_get_images - return table of available image devices userdata
//  -> manager:machine().images["flop1"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_machine_get_images(const running_machine *r)
{
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef image_table = luabridge::LuaRef::newTable(L);

	for (device_image_interface &image : image_interface_iterator(r->root_device()))
	{
		image_table[image.brief_instance_name()] = &image;
		image_table[image.instance_name()] = &image;
	}

	return image_table;
}

//-------------------------------------------------
//  memory_banks - return memory_banks
//  -> manager:machine():memory().banks["maincpu"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_memory_get_banks(const memory_manager *m)
{
	memory_manager *mm = const_cast<memory_manager *>(m);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef table = luabridge::LuaRef::newTable(L);

	for (auto &bank : mm->banks()) {
		table[bank.second->tag()] = bank.second.get();
	}

	return table;
}

//-------------------------------------------------
//  memory_regions - return memory_regions
//  -> manager:machine():memory().region[":maincpu"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_memory_get_regions(const memory_manager *m)
{
	memory_manager *mm = const_cast<memory_manager *>(m);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef table = luabridge::LuaRef::newTable(L);

	for (auto &region: mm->regions()) {
		table[region.second->name()] = region.second.get();
	}

	return table;
}

//-------------------------------------------------
//  memory_shares - return memory_shares
//  -> manager:machine():memory().share[":maincpu"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_memory_get_shares(const memory_manager *m)
{
	memory_manager *mm = const_cast<memory_manager *>(m);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef table = luabridge::LuaRef::newTable(L);

	for (auto &share: mm->shares()) {
		table[share.first] = share.second.get();
	}

	return table;
}

//-------------------------------------------------
//  machine_cheat_entries - return cheat entries
//  -> manager:machine():cheat().entries[0]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_cheat_get_entries(const cheat_manager *c)
{
	cheat_manager *cm = const_cast<cheat_manager *>(c);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef entry_table = luabridge::LuaRef::newTable(L);

	int cheatnum = 0;
	for (auto &entry : cm->entries()) {
		entry_table[cheatnum++] = entry.get();
	}

	return entry_table;
}

//-------------------------------------------------
//  cheat_entry_state - return cheat entry state
//  -> manager:machine():cheat().entries[0]:state()
//-------------------------------------------------

int lua_engine::lua_cheat_entry::l_get_state(lua_State *L)
{
	cheat_entry *ce = luabridge::Stack<cheat_entry *>::get(L, 1);

	switch (ce->state())
	{
		case SCRIPT_STATE_ON:     lua_pushliteral(L, "on"); break;
		case SCRIPT_STATE_RUN:    lua_pushliteral(L, "run"); break;
		case SCRIPT_STATE_CHANGE: lua_pushliteral(L, "change"); break;
		case SCRIPT_STATE_COUNT:  lua_pushliteral(L, "count"); break;
		default:                  lua_pushliteral(L, "off"); break;
	}

	return 1;
}

//-------------------------------------------------
//  machine_ioports - return table of ioports
//  -> manager:machine():ioport().ports[':P1']
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_ioport_get_ports(const ioport_manager *m)
{
	ioport_manager *im = const_cast<ioport_manager *>(m);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef port_table = luabridge::LuaRef::newTable(L);

	for (auto &port : im->ports()) {
		port_table[port.second->tag()] = port.second.get();
	}

	return port_table;
}

//-------------------------------------------------
//  ioport_fields - return table of ioport fields
//  -> manager:machine().ioport().ports[':P1'].fields[':']
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_ioports_port_get_fields(const ioport_port *i)
{
	ioport_port *p = const_cast<ioport_port *>(i);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef f_table = luabridge::LuaRef::newTable(L);

	for (ioport_field &field : p->fields()) {
		if (field.type_class() != INPUT_CLASS_INTERNAL)
			f_table[field.name()] = &field;
	}

	return f_table;
}

//-------------------------------------------------
//  render_get_targets - return table of render targets
//  -> manager:machine():render().targets[0]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_render_get_targets(const render_manager *r)
{
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef target_table = luabridge::LuaRef::newTable(L);

	int tc = 0;
	for (render_target &curr_rt : r->targets())
	{
		target_table[tc++] = &curr_rt;
	}

	return target_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->subdevices()) {
			if (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);
	device_memory_interface *memdev = dynamic_cast<device_memory_interface *>(dev);

	if(!memdev)
		return sp_table;

	for (address_spacenum sp = AS_0; sp < ADDRESS_SPACES; ++sp) {
		if (memdev->has_space(sp)) {
			sp_table[memdev->space(sp).name()] = lua_addr_space(&memdev->space(sp), memdev);
		}
	}

	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);

	if(!dynamic_cast<device_state_interface *>(dev))
		return st_table;

	for (auto &s : dev->state().state_entries())
	{
		// XXX: refrain from exporting non-visible entries?
		st_table[s->symbol()] = s.get();
	}

	return st_table;
}

//-------------------------------------------------
//  device_get_item - return table of indexed items owned by this device
//  -> manager:machine().devices[":maincpu"].items
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_dev_get_items(const device_t *d)
{
	device_t *dev = const_cast<device_t *>(d);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef table = luabridge::LuaRef::newTable(L);
	std::string tag = dev->tag();

	// 10000 is enough?
	for(int i = 0; i < 10000; i++)
	{
		std::string name;
		const char *item;
		unsigned int size, count;
		void *base;
		item = dev->machine().save().indexed_item(i, base, size, count);
		if(!item)
			break;
		name = &(strchr(item, '/')[1]);
		if(name.substr(0, name.find("/")) == tag)
		{
			name = name.substr(name.find("/") + 1, std::string::npos);
			table[name] = i;
		}
	}
	return table;
}

lua_engine::lua_item::lua_item(int index)
{
	std::string name;
	const char *item;
	item = luaThis->machine().save().indexed_item(index, l_item_base, l_item_size, l_item_count);
	if(!item)
	{
		l_item_base = nullptr;
		l_item_size = 0;
		l_item_count= 0;
	}
}

int lua_engine::lua_item::l_item_read(lua_State *L)
{
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "offset (integer) expected");
	int offset = lua_tounsigned(L, 2);
	int ret = 0;
	if(!l_item_base || (offset > l_item_count))
	{
		lua_pushnil(L);
		return 1;
	}
	switch(l_item_size)
	{
		case 1:
		default:
			ret = ((UINT8 *)l_item_base)[offset];
			break;
		case 2:
			ret = ((UINT16 *)l_item_base)[offset];
			break;
		case 4:
			ret = ((UINT32 *)l_item_base)[offset];
			break;
		case 8:
			ret = ((UINT64 *)l_item_base)[offset];
			break;
	}
	lua_pushunsigned(L, ret);
	return 1;
}

int lua_engine::lua_item::l_item_write(lua_State *L)
{
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "offset (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected");
	int offset = lua_tounsigned(L, 2);
	UINT64 value = lua_tounsigned(L, 3);
	if(!l_item_base || (offset > l_item_count))
		return 1;
	switch(l_item_size)
	{
		case 1:
		default:
			((UINT8 *)l_item_base)[offset] = (UINT8)value;
			break;
		case 2:
			((UINT16 *)l_item_base)[offset] = (UINT16)value;
			break;
		case 4:
			((UINT32 *)l_item_base)[offset] = (UINT32)value;
			break;
		case 8:
			((UINT64 *)l_item_base)[offset] = (UINT64)value;
			break;
	}
	return 1;
}

int lua_engine::lua_item::l_item_read_block(lua_State *L)
{
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "offset (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "length (integer) expected");
	int offset = lua_tounsigned(L, 2);
	int len = lua_tonumber(L, 3);
	if(!l_item_base || ((offset + len) > (l_item_size * l_item_count)))
	{
		lua_pushnil(L);
		return 1;
	}
	luaL_Buffer buff;
	char *ptr = luaL_buffinitsize(L, &buff, len);
	memcpy(ptr, l_item_base, len);
	luaL_pushresultsize(&buff, len);
	return 1;
}

//-------------------------------------------------
//  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)
{
	lua_addr_space &lsp = luabridge::Stack<lua_addr_space &>::get(L, 1);
	address_space &sp = lsp.space;
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected");
	luaL_argcheck(L, lua_isboolean(L, 3) || lua_isnone(L, 3), 3, "optional argument: disable address shift (bool) expected");
	offs_t address = lua_tounsigned(L, 2);
	T mem_content = 0;
	if(!lua_toboolean(L, 3))
		address = sp.address_to_byte(address);
	switch(sizeof(mem_content) * 8) {
		case 8:
			mem_content = sp.read_byte(address);
			break;
		case 16:
			if (WORD_ALIGNED(address)) {
				mem_content = sp.read_word(address);
			} else {
				mem_content = sp.read_word_unaligned(address);
			}
			break;
		case 32:
			if (DWORD_ALIGNED(address)) {
				mem_content = sp.read_dword(address);
			} else {
				mem_content = sp.read_dword_unaligned(address);
			}
			break;
		case 64:
			if (QWORD_ALIGNED(address)) {
				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)
{
	lua_addr_space &lsp = luabridge::Stack<lua_addr_space &>::get(L, 1);
	address_space &sp = lsp.space;
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected");
	luaL_argcheck(L, lua_isboolean(L, 4) || lua_isnone(L, 4), 4, "optional argument: disable address shift (bool) expected");
	offs_t address = lua_tounsigned(L, 2);
	T val = lua_tounsigned(L, 3);
	if(!lua_toboolean(L, 4))
		address = sp.address_to_byte(address);
	switch(sizeof(val) * 8) {
		case 8:
			sp.write_byte(address, val);
			break;
		case 16:
			if (WORD_ALIGNED(address)) {
				sp.write_word(address, val);
			} else {
				sp.write_word_unaligned(address, val);
			}
			break;
		case 32:
			if (DWORD_ALIGNED(address)) {
				sp.write_dword(address, val);
			} else {
				sp.write_dword_unaligned(address, val);
			}
			break;
		case 64:
			if (QWORD_ALIGNED(address)) {
				sp.write_qword(address, val);
			} else {
				sp.write_qword_unaligned(address, val);
			}
			break;
		default:
			break;
	}

	return 0;
}

//-------------------------------------------------
//  log_mem_read - templated logical memory readers for <sign>,<size>
//  -> manager:machine().devices[":maincpu"].spaces["program"]:read_log_i8(0xC000)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_addr_space::l_log_mem_read(lua_State *L)
{
	lua_addr_space &lsp = luabridge::Stack<lua_addr_space &>::get(L, 1);
	address_space &sp = lsp.space;
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected");
	offs_t address = lua_tounsigned(L, 2);
	T mem_content = 0;
	if(!lsp.dev->translate(sp.spacenum(), TRANSLATE_READ_DEBUG, address))
		return 0;
	address = sp.address_to_byte(address);

	switch(sizeof(mem_content) * 8) {
		case 8:
			mem_content = sp.read_byte(address);
			break;
		case 16:
			if (WORD_ALIGNED(address)) {
				mem_content = sp.read_word(address);
			} else {
				mem_content = sp.read_word_unaligned(address);
			}
			break;
		case 32:
			if (DWORD_ALIGNED(address)) {
				mem_content = sp.read_dword(address);
			} else {
				mem_content = sp.read_dword_unaligned(address);
			}
			break;
		case 64:
			if (QWORD_ALIGNED(address)) {
				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;

}

//-------------------------------------------------
//  log_mem_write - templated logical memory writer for <sign>,<size>
//  -> manager:machine().devices[":maincpu"].spaces["program"]:write_log_u16(0xC000, 0xF00D)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_addr_space::l_log_mem_write(lua_State *L)
{
	lua_addr_space &lsp = luabridge::Stack<lua_addr_space &>::get(L, 1);
	address_space &sp = lsp.space;
	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);

	if(!lsp.dev->translate(sp.spacenum(), TRANSLATE_WRITE_DEBUG, address))
		return 0;
	address = sp.address_to_byte(address);

	switch(sizeof(val) * 8) {
		case 8:
			sp.write_byte(address, val);
			break;
		case 16:
			if (WORD_ALIGNED(address)) {
				sp.write_word(address, val);
			} else {
				sp.write_word_unaligned(address, val);
			}
			break;
		case 32:
			if (DWORD_ALIGNED(address)) {
				sp.write_dword(address, val);
			} else {
				sp.write_dword_unaligned(address, val);
			}
			break;
		case 64:
			if (QWORD_ALIGNED(address)) {
				sp.write_qword(address, val);
			} else {
				sp.write_qword_unaligned(address, val);
			}
			break;
		default:
			break;
	}

	return 0;
}

//-------------------------------------------------
//  mem_direct_read - templated direct memory readers for <sign>,<size>
//  -> manager:machine().devices[":maincpu"].spaces["program"]:read_direct_i8(0xC000)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_addr_space::l_direct_mem_read(lua_State *L)
{
	lua_addr_space &lsp = luabridge::Stack<lua_addr_space &>::get(L, 1);
	address_space &sp = lsp.space;
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected");
	offs_t address = lua_tounsigned(L, 2);
	T mem_content = 0;
	offs_t lowmask = sp.data_width() / 8 - 1;
	for(int i = 0; i < sizeof(T); i++)
	{
		int addr = sp.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
		UINT8 *base = (UINT8 *)sp.get_read_ptr(sp.address_to_byte(addr & ~lowmask));
		if(!base)
			continue;
		mem_content <<= 8;
		if(sp.endianness() == ENDIANNESS_BIG)
			mem_content |= base[BYTE8_XOR_BE(addr) & lowmask];
		else
			mem_content |= base[BYTE8_XOR_LE(addr) & lowmask];
	}

	if (std::numeric_limits<T>::is_signed) {
		lua_pushinteger(L, mem_content);
	} else {
		lua_pushunsigned(L, mem_content);
	}

	return 1;
}

//-------------------------------------------------
//  mem_direct_write - templated memory writer for <sign>,<size>
//  -> manager:machine().devices[":maincpu"].spaces["program"]:write_direct_u16(0xC000, 0xF00D)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_addr_space::l_direct_mem_write(lua_State *L)
{
	lua_addr_space &lsp = luabridge::Stack<lua_addr_space &>::get(L, 1);
	address_space &sp = lsp.space;
	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);
	offs_t lowmask = sp.data_width() / 8 - 1;
	for(int i = 0; i < sizeof(T); i++)
	{
		int addr = sp.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
		UINT8 *base = (UINT8 *)sp.get_read_ptr(sp.address_to_byte(addr & ~lowmask));
		if(!base)
			continue;
		if(sp.endianness() == ENDIANNESS_BIG)
			base[BYTE8_XOR_BE(addr) & lowmask] = val & 0xff;
		else
			base[BYTE8_XOR_LE(addr) & lowmask] = val & 0xff;
		val >>= 8;
	}

	return 0;
}

//-------------------------------------------------
//  region_read - templated region readers for <sign>,<size>
//  -> manager:machine():memory().regions[":maincpu"]:read_i8(0xC000)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_memory_region::l_region_read(lua_State *L)
{
	memory_region &region = luabridge::Stack<memory_region &>::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;
	offs_t lowmask = region.bytewidth() - 1;
	for(int i = 0; i < sizeof(T); i++)
	{
		int addr = region.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
		if(addr >= region.bytes())
			continue;
		mem_content <<= 8;
		if(region.endianness() == ENDIANNESS_BIG)
			mem_content |= region.u8((BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask));
		else
			mem_content |= region.u8((BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask));
	}

	if (std::numeric_limits<T>::is_signed) {
		lua_pushinteger(L, mem_content);
	} else {
		lua_pushunsigned(L, mem_content);
	}

	return 1;
}

//-------------------------------------------------
//  region_write - templated region writer for <sign>,<size>
//  -> manager:machine():memory().regions[":maincpu"]:write_u16(0xC000, 0xF00D)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_memory_region::l_region_write(lua_State *L)
{
	memory_region &region = luabridge::Stack<memory_region &>::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);
	offs_t lowmask = region.bytewidth() - 1;
	for(int i = 0; i < sizeof(T); i++)
	{
		int addr = region.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
		if(addr >= region.bytes())
			continue;
		if(region.endianness() == ENDIANNESS_BIG)
			region.base()[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
		else
			region.base()[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
		val >>= 8;
	}

	return 0;
}

//-------------------------------------------------
//  share_read - templated share readers for <sign>,<size>
//  -> manager:machine():memory().shares[":maincpu"]:read_i8(0xC000)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_memory_share::l_share_read(lua_State *L)
{
	memory_share &share = luabridge::Stack<memory_share &>::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;
	offs_t lowmask = share.bytewidth() - 1;
	UINT8* ptr = (UINT8*)share.ptr();
	for(int i = 0; i < sizeof(T); i++)
	{
		int addr = share.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
		if(addr >= share.bytes())
			continue;
		mem_content <<= 8;
		if(share.endianness() == ENDIANNESS_BIG)
			mem_content |= ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)];
		else
			mem_content |= ptr[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)];
	}

	if (std::numeric_limits<T>::is_signed) {
		lua_pushinteger(L, mem_content);
	} else {
		lua_pushunsigned(L, mem_content);
	}

	return 1;
}

//-------------------------------------------------
//  share_write - templated share writer for <sign>,<size>
//  -> manager:machine():memory().shares[":maincpu"]:write_u16(0xC000, 0xF00D)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_memory_share::l_share_write(lua_State *L)
{
	memory_share &share = luabridge::Stack<memory_share &>::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);
	offs_t lowmask = share.bytewidth() - 1;
	UINT8* ptr = (UINT8*)share.ptr();
	for(int i = 0; i < sizeof(T); i++)
	{
		int addr = share.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
		if(addr >= share.bytes())
			continue;
		if(share.endianness() == ENDIANNESS_BIG)
			ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
		else
			ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
		val >>= 8;
	}

	return 0;
}

luabridge::LuaRef lua_engine::l_addr_space_map(const lua_addr_space *sp)
{
	address_space &space = sp->space;
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef map = luabridge::LuaRef::newTable(L);

	int i = 1;
	for (address_map_entry &entry : space.map()->m_entrylist)
	{
		luabridge::LuaRef mapentry = luabridge::LuaRef::newTable(L);
		mapentry["offset"] = space.address_to_byte(entry.m_addrstart) & space.bytemask();
		mapentry["endoff"] = space.address_to_byte(entry.m_addrend) & space.bytemask();
		mapentry["readtype"] = entry.m_read.m_type;
		mapentry["writetype"] = entry.m_write.m_type;
		map[i++] = mapentry;
	}
	return map;
}

int lua_engine::lua_options_entry::l_entry_value(lua_State *L)
{
	core_options::entry *e = luabridge::Stack<core_options::entry *>::get(L, 1);
	if(!e) {
		return 0;
	}

	luaL_argcheck(L, !lua_isfunction(L, 2), 2, "optional argument: unsupported value");

	if (!lua_isnone(L, 2))
	{
		std::string error;
		// FIXME: not working with ui_options::entry
		// TODO: optional arg for priority
		luaThis->machine().options().set_value(e->name(),
				lua_isboolean(L, 2) ? (lua_toboolean(L, 2) ? "1" : "0") : lua_tostring(L, 2),
				OPTION_PRIORITY_CMDLINE, error);

		if (!error.empty())
		{
			luaL_error(L, "%s", error.c_str());
		}
	}

	switch (e->type())
	{
		case OPTION_BOOLEAN:
			lua_pushboolean(L, (atoi(e->value()) != 0));
			break;
		case OPTION_INTEGER:
			lua_pushnumber(L, atoi(e->value()));
			break;
		case OPTION_FLOAT:
			lua_pushnumber(L, atof(e->value()));
			break;
		default:
			lua_pushstring(L, e->value());
			break;
	}

	return 1;
}

//-------------------------------------------------
//  begin_recording - start avi
//  -> manager:machine():video():begin_recording()
//-------------------------------------------------

int lua_engine::lua_video::l_begin_recording(lua_State *L)
{
	video_manager *vm = luabridge::Stack<video_manager *>::get(L, 1);
	if (!vm) {
		return 0;
	}

	luaL_argcheck(L, lua_isstring(L, 2) || lua_isnone(L, 2), 2, "optional argument: filename, string expected");

	const char *filename = lua_tostring(L, 2);
	if (!lua_isnone(L, 2)) {
		std::string vidname(filename);
		strreplace(vidname, "/", PATH_SEPARATOR);
		strreplace(vidname, "%g", luaThis->machine().basename());
		filename = vidname.c_str();
	} else {
		filename = nullptr;
	}
	vm->begin_recording(filename, video_manager::MF_AVI);

	return 1;
}

//-------------------------------------------------
//  end_recording - start saving avi
//  -> manager:machine():video():end_recording()
//-------------------------------------------------

int lua_engine::lua_video::l_end_recording(lua_State *L)
{
	video_manager *vm = luabridge::Stack<video_manager *>::get(L, 1);
	if (!vm) {
		return 0;
	}

	if (!vm->is_recording()) {
		lua_writestringerror("%s", "No active recording to stop");
		return 0;
	}

	vm->end_recording(video_manager::MF_AVI);
	return 1;
}

//-------------------------------------------------
//  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;
}


//-------------------------------------------------
//  screen_orientation - return screen orientation
//  -> manager:machine().screens[":screen"]:orientation()
//     -> rotation_angle (0, 90, 180, 270)
//     -> flipx (true, false)
//     -> flipy (true, false)
//-------------------------------------------------

int lua_engine::lua_screen::l_orientation(lua_State *L)
{
	UINT32 flags = (luaThis->machine().system().flags & ORIENTATION_MASK);

	int rotation_angle = 0;
	switch (flags)
	{
		case ORIENTATION_FLIP_X:
			rotation_angle = 0;
			break;
		case ORIENTATION_SWAP_XY:
		case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_X:
			rotation_angle = 90;
			break;
		case ORIENTATION_FLIP_Y:
		case ORIENTATION_FLIP_X|ORIENTATION_FLIP_Y:
			rotation_angle = 180;
			break;
		case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_Y:
		case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_X|ORIENTATION_FLIP_Y:
			rotation_angle = 270;
			break;
	}

	lua_createtable(L, 2, 2);
	lua_pushliteral(L, "rotation_angle");
	lua_pushinteger(L, rotation_angle);

	lua_settable(L, -3);
	lua_pushliteral(L, "flipx");
	lua_pushboolean(L, (flags & ORIENTATION_FLIP_X));

	lua_settable(L, -3);
	lua_pushliteral(L, "flipy");
	lua_pushboolean(L, (flags & ORIENTATION_FLIP_Y));
	lua_settable(L, -3);
	return 1;
}

//-------------------------------------------------
//  screen_refresh - return screen refresh rate
//  -> manager:machine().screens[":screen"]:refresh()
//-------------------------------------------------

int lua_engine::lua_screen::l_refresh(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	lua_pushnumber(L, ATTOSECONDS_TO_HZ(sc->refresh_attoseconds()));
	return 1;
}

//-------------------------------------------------
//  screen_snapshot - save png bitmap of screen to snapshots folder
//  -> manager:machine().screens[":screen"]:snapshot("filename.png")
//-------------------------------------------------

int lua_engine::lua_screen::l_snapshot(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc || !sc->machine().render().is_live(*sc))
	{
		return 0;
	}

	luaL_argcheck(L, lua_isstring(L, 2) || lua_isnone(L, 2), 2, "optional argument: filename, string expected");

	emu_file file(sc->machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
	osd_file::error filerr;

	if (!lua_isnone(L, 2)) {
		const char *filename = lua_tostring(L, 2);
		std::string snapstr(filename);
		strreplace(snapstr, "/", PATH_SEPARATOR);
		strreplace(snapstr, "%g", sc->machine().basename());
		filerr = file.open(snapstr.c_str());
	}
	else
	{
		filerr = sc->machine().video().open_next(file, "png");
	}

	if (filerr != osd_file::error::NONE)
	{
		luaL_error(L, "osd_file::error=%d", filerr);
		return 0;
	}

	sc->machine().video().save_snapshot(sc, file);
	lua_writestringerror("saved %s", file.fullpath());
	file.close();
	return 1;
}

//-------------------------------------------------
//  screen_type - return human readable screen type
//  -> manager:machine().screens[":screen"]:type()
//-------------------------------------------------

int lua_engine::lua_screen::l_type(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	switch (sc->screen_type())
	{
		case SCREEN_TYPE_RASTER:  lua_pushliteral(L, "raster"); break;
		case SCREEN_TYPE_VECTOR:  lua_pushliteral(L, "vector"); break;
		case SCREEN_TYPE_LCD:     lua_pushliteral(L, "lcd"); break;
		case SCREEN_TYPE_SVG:     lua_pushliteral(L, "svg"); break;
		default:                  lua_pushliteral(L, "unknown"); break;
	}

	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();
	mame_machine_manager::instance()->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
//  if x is a position, then y is a pixel position, otherwise x and y are screen size relative
//  -> 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) || lua_isstring(L, 2), 2, "x (integer or string) 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();
	auto justify = ui::text_layout::LEFT;
	float y, x = 0;
	if(lua_isnumber(L, 2))
	{
		x = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width);
		y = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height);
	}
	else
	{
		std::string just_str = lua_tostring(L, 2);
		if(just_str == "right")
			justify = ui::text_layout::RIGHT;
		else if(just_str == "center")
			justify = ui::text_layout::CENTER;
		y = lua_tonumber(L, 3);
	}
	const char *msg = luaL_checkstring(L,4);
	rgb_t textcolor = UI_TEXT_COLOR;
	rgb_t bgcolor = UI_TEXT_BG_COLOR;
	if (!lua_isnone(L, 5)) {
		textcolor = rgb_t(lua_tounsigned(L, 5));
	}

	// draw the text
	render_container &rc = sc->container();
	mame_machine_manager::instance()->ui().draw_text_full(rc, msg, x, y, (1.0f - x),
						justify, ui::text_layout::WORD, mame_ui_manager::NORMAL, textcolor,
						bgcolor, nullptr, nullptr);
	return 0;
}

int lua_engine::lua_emu_file::l_emu_file_read(lua_State *L)
{
	lua_emu_file *file = luabridge::Stack<lua_emu_file *>::get(L, 1);
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "length (integer) expected");
	int ret, len = lua_tonumber(L, 2);
	luaL_Buffer buff;
	char *ptr = luaL_buffinitsize(L, &buff, len);
	ret = file->file.read(ptr, len);
	luaL_pushresultsize(&buff, ret);
	return 1;
}

int lua_engine::lua_ui_input::l_ui_input_find_mouse(lua_State *L)
{
	ui_input_manager *ui_input = luabridge::Stack<ui_input_manager *>::get(L, 1);
	INT32 x, y;
	bool button;
	render_target *target = ui_input->find_mouse(&x, &y, &button);
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_pushboolean(L, button);
	luabridge::Stack<render_target *>::push(L, target);
	return 4;
}

int lua_engine::lua_render_target::l_render_view_bounds(lua_State *L)
{
	render_target *target = luabridge::Stack<render_target *>::get(L, 1);
	const render_bounds &bounds = target->current_view()->bounds();
	lua_pushnumber(L, bounds.x0);
	lua_pushnumber(L, bounds.x1);
	lua_pushnumber(L, bounds.y0);
	lua_pushnumber(L, bounds.y1);
	return 4;
}

void *lua_engine::checkparam(lua_State *L, int idx, const char *tname)
{
	const char *name;

	if(!lua_getmetatable(L, idx))
	return nullptr;

	lua_rawget(L, LUA_REGISTRYINDEX);
	name = lua_tostring(L, -1);
	if(!name || strcmp(name, tname)) {
	lua_pop(L, 1);
	return nullptr;
	}
	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) {
		mame_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 },
		{ nullptr, nullptr }  /* 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;

static std::mutex g_mutex;

void lua_engine::serve_lua()
{
	osd_sleep(osd_ticks_per_second() / 1000 * 50);
	printf("%s v%s\n%s\n%s\n\n", emulator_info::get_appname(),build_version,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
		{
			std::lock_guard<std::mutex> lock(g_mutex);
			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;
			}
		}

		// Wait for response
		int done;
		do {
			osd_sleep(osd_ticks_per_second() / 1000);
			std::lock_guard<std::mutex> lock(g_mutex);
			done = msg.done;
		} while (done==0);

		// Do action on client side
		{
			std::lock_guard<std::mutex> lock(g_mutex);

			if (msg.status == -1) {
				b = LUA_PROMPT2;
				oldbuff = msg.response;
			}
			else {
				b = LUA_PROMPT;
				oldbuff = "";
			}
			msg.done = 0;
		}

	} while (1);
}

static void *serve_lua(void *param)
{
	lua_engine *engine = (lua_engine *)param;
	engine->serve_lua();
	return nullptr;
}

//-------------------------------------------------
//  lua_engine - constructor
//-------------------------------------------------

lua_engine::lua_engine()
{
	m_machine = nullptr;
	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 */

		// Get package.preload so we can store builtins in it.
	lua_getglobal(m_lua_state, "package");
	lua_getfield(m_lua_state, -1, "preload");
	lua_remove(m_lua_state, -2); // Remove package

	lua_pushcfunction(m_lua_state, luaopen_zlib);
	lua_setfield(m_lua_state, -2, "zlib");

	lua_pushcfunction(m_lua_state, luaopen_lfs);
	lua_setfield(m_lua_state, -2, "lfs");

	luaopen_ioport(m_lua_state);

	lua_gc(m_lua_state, LUA_GCRESTART, 0);
	msg.ready = 0;
	msg.status = 0;
	msg.done = 0;
}

//-------------------------------------------------
//  ~lua_engine - destructor
//-------------------------------------------------

lua_engine::~lua_engine()
{
	close();
}

void lua_engine::call_plugin(const char *data, const char *name)
{
	std::string field("cb_");
	field += name;
	lua_settop(m_lua_state, 0);
	lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str());

	if(!lua_isfunction(m_lua_state, -1))
	{
		lua_pop(m_lua_state, 1);
		return;
	}
	lua_pushstring(m_lua_state, data);
	int error;
	if((error = lua_pcall(m_lua_state, 1, 0, 0)) != LUA_OK)
	{
		if(error == LUA_ERRRUN)
			printf("%s\n", lua_tostring(m_lua_state, -1));
		lua_pop(m_lua_state, 1);
		return;
	}
}

int lua_engine::l_emu_register_callback(lua_State *L)
{
	luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected");
	luaL_argcheck(L, lua_isstring(L, 2), 2, "name (string) expected");
	std::string name = luaL_checkstring(L, 2);
	std::string field = "cb_" + name;
	lua_pushvalue(L, 1);
	lua_setfield(L, LUA_REGISTRYINDEX, field.c_str());
	return 1;
}

void lua_engine::menu_populate(std::string &menu, std::vector<menu_item> &menu_list)
{
	std::string field = "menu_pop_" + menu;
	lua_settop(m_lua_state, 0);
	lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str());

	if(!lua_isfunction(m_lua_state, -1))
	{
		lua_pop(m_lua_state, 1);
		return;
	}
	int error;
	if((error = lua_pcall(m_lua_state, 0, 1, 0)) != LUA_OK)
	{
		if(error == LUA_ERRRUN)
			printf("%s\n", lua_tostring(m_lua_state, -1));
		lua_pop(m_lua_state, 1);
		return;
	}
	if(!lua_istable(m_lua_state, -1))
	{
		lua_pop(m_lua_state, 1);
		return;
	}

	lua_pushnil(m_lua_state);
	while(lua_next(m_lua_state, -2))
	{
		if(lua_istable(m_lua_state, -1))
		{
			menu_item item;
			lua_rawgeti(m_lua_state, -1, 1);
			item.text = lua_tostring(m_lua_state, -1);
			lua_pop(m_lua_state, 1);
			lua_rawgeti(m_lua_state, -1, 2);
			item.subtext = lua_tostring(m_lua_state, -1);
			lua_pop(m_lua_state, 1);
			lua_rawgeti(m_lua_state, -1, 3);
			item.flags = lua_tostring(m_lua_state, -1);
			lua_pop(m_lua_state, 1);
			menu_list.push_back(item);
		}
		lua_pop(m_lua_state, 1);
	}
	lua_pop(m_lua_state, 1);
}

bool lua_engine::menu_callback(std::string &menu, int index, std::string event)
{
	std::string field = "menu_cb_" + menu;
	bool ret = false;
	lua_settop(m_lua_state, 0);
	lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str());

	if(lua_isfunction(m_lua_state, -1))
	{
		lua_pushinteger(m_lua_state, index);
		lua_pushstring(m_lua_state, event.c_str());
		int error;
		if((error = lua_pcall(m_lua_state, 2, 1, 0)) != LUA_OK)
		{
			if(error == 2)
				printf("%s\n", lua_tostring(m_lua_state, -1));
			lua_pop(m_lua_state, 1);
			return false;
		}
		ret = lua_toboolean(m_lua_state, -1);
		lua_pop(m_lua_state, 1);
	}
	return ret;
}

int lua_engine::l_emu_register_menu(lua_State *L)
{
	luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected");
	luaL_argcheck(L, lua_isfunction(L, 2), 2, "callback function expected");
	luaL_argcheck(L, lua_isstring(L, 3), 3, "name (string) expected");
	std::string name = luaL_checkstring(L, 3);
	std::string cbfield = "menu_cb_" + name;
	std::string popfield = "menu_pop_" + name;
	luaThis->m_menu.push_back(std::string(name));
	lua_pushvalue(L, 1);
	lua_setfield(L, LUA_REGISTRYINDEX, cbfield.c_str());
	lua_pushvalue(L, 2);
	lua_setfield(L, LUA_REGISTRYINDEX, popfield.c_str());
	return 1;
}

void lua_engine::execute_function(const char *id)
{
	lua_settop(m_lua_state, 0);
	lua_getfield(m_lua_state, LUA_REGISTRYINDEX, id);

	if (lua_istable(m_lua_state, -1))
	{
		lua_pushnil(m_lua_state);
		while (lua_next(m_lua_state, -2) != 0)
		{
			if (lua_isfunction(m_lua_state, -1))
			{
				int error;
				if((error = lua_pcall(m_lua_state, 0, 0, 0)) != LUA_OK)
				{
					if(error == 2)
						printf("%s\n", lua_tostring(m_lua_state, -1));
					lua_pop(m_lua_state, 1);
				}
			}
			else
			{
				lua_pop(m_lua_state, 1);
			}
		}
	}
}

int lua_engine::register_function(lua_State *L, const char *id)
{
	if (!lua_isnil(L, 1))
		luaL_checktype(L, 1, LUA_TFUNCTION);
	lua_settop(L, 1);
	lua_getfield(L, LUA_REGISTRYINDEX, id);
	if (lua_isnil(L, -1))
	{
		lua_newtable(L);
	}
	luaL_checktype(L, -1, LUA_TTABLE);
	int len = lua_rawlen(L, -1);
	lua_pushnumber(L, len + 1);
	lua_pushvalue(L, 1);
	lua_rawset(L, -3);      /* Stores the pair in the table */

	lua_pushvalue(L, -1);
	lua_setfield(L, LUA_REGISTRYINDEX, id);
	return 1;
}

int lua_engine::l_emu_register_prestart(lua_State *L)
{
	return register_function(L, "LUA_ON_PRESTART");
}

int lua_engine::l_emu_register_start(lua_State *L)
{
	return register_function(L, "LUA_ON_START");
}

int lua_engine::l_emu_register_stop(lua_State *L)
{
	return register_function(L, "LUA_ON_STOP");
}

int lua_engine::l_emu_register_pause(lua_State *L)
{
	return register_function(L, "LUA_ON_PAUSE");
}

int lua_engine::l_emu_register_resume(lua_State *L)
{
	return register_function(L, "LUA_ON_RESUME");
}

int lua_engine::l_emu_register_frame(lua_State *L)
{
	return register_function(L, "LUA_ON_FRAME");
}

int lua_engine::l_emu_register_frame_done(lua_State *L)
{
	return register_function(L, "LUA_ON_FRAME_DONE");
}

void lua_engine::on_machine_prestart()
{
	execute_function("LUA_ON_PRESTART");
}

void lua_engine::on_machine_start()
{
	execute_function("LUA_ON_START");
}

void lua_engine::on_machine_stop()
{
	execute_function("LUA_ON_STOP");
}

void lua_engine::on_machine_pause()
{
	execute_function("LUA_ON_PAUSE");
}

void lua_engine::on_machine_resume()
{
	execute_function("LUA_ON_RESUME");
}

void lua_engine::on_machine_frame()
{
	execute_function("LUA_ON_FRAME");
}

void lua_engine::on_frame_done()
{
	execute_function("LUA_ON_FRAME_DONE");
}

void lua_engine::update_machine()
{
	lua_newtable(m_lua_state);
	if (m_machine!=nullptr)
	{
		// Create the ioport array
		for (auto &port : machine().ioport().ports())
		{
			for (ioport_field &field : port.second->fields())
			{
				if (field.type_class() != INPUT_CLASS_INTERNAL)
				{
					push(m_lua_state, &field, tname_ioport);
					lua_setfield(m_lua_state, -2, field.name());
				}
			}
		}
	}
	lua_setglobal(m_lua_state, "ioport");
}

void lua_engine::attach_notifiers()
{
	machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(FUNC(lua_engine::on_machine_prestart), this), true);
	machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(FUNC(lua_engine::on_machine_start), this));
	machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(lua_engine::on_machine_stop), this));
	machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(FUNC(lua_engine::on_machine_pause), this));
	machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(FUNC(lua_engine::on_machine_resume), this));
	machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(FUNC(lua_engine::on_machine_frame), this));
}

int lua_engine::lua_machine::l_popmessage(lua_State *L)
{
	running_machine *m = luabridge::Stack<running_machine *>::get(L, 1);
	if(!lua_isstring(L, 2))
		m->popmessage();
	else
		m->popmessage("%s", luaL_checkstring(L, 2));
	return 0;
}

int lua_engine::lua_machine::l_logerror(lua_State *L)
{
	running_machine *m = luabridge::Stack<running_machine *>::get(L, 1);
	luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected");
	m->logerror("[luaengine] %s\n", luaL_checkstring(L, 2));
	return 0;
}

std::string lua_engine::get_print_buffer(lua_State *L)
{
	int nargs = lua_gettop(L);

	const std::string sep = " ";

	std::ostringstream ss;
	bool first = true;

	for (int i = 1; i <= nargs; i++) {
		const char* c = lua_tostring(L, i);
		const std::string str = c ? c : "<nil>";
		if (first) first = false;
		else ss << sep;
		ss << str;
	}

	return ss.str();
}
int lua_engine::l_osd_printf_verbose(lua_State *L)
{
	osd_printf_verbose("%s\n",get_print_buffer(L).c_str());
	return 0;
}

int lua_engine::l_osd_printf_error(lua_State *L)
{
	osd_printf_error("%s\n",get_print_buffer(L).c_str());
	return 0;
}

int lua_engine::l_osd_printf_info(lua_State *L)
{
	osd_printf_info("%s\n",get_print_buffer(L).c_str());
	return 0;
}

int lua_engine::l_osd_printf_debug(lua_State *L)
{
	osd_printf_debug("%s\n",get_print_buffer(L).c_str());
	return 0;
}

//-------------------------------------------------
//  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 ("softname",    l_emu_softname )
			.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 )
			.addCFunction ("register_prestart", l_emu_register_prestart )
			.addCFunction ("register_start", l_emu_register_start )
			.addCFunction ("register_stop",  l_emu_register_stop )
			.addCFunction ("register_pause", l_emu_register_pause )
			.addCFunction ("register_resume",l_emu_register_resume )
			.addCFunction ("register_frame", l_emu_register_frame )
			.addCFunction ("register_frame_done", l_emu_register_frame_done )
			.addCFunction ("register_menu",  l_emu_register_menu )
			.addCFunction ("register_callback",  l_emu_register_callback )
			.addCFunction ("print_verbose", l_osd_printf_verbose )
			.addCFunction ("print_error",   l_osd_printf_error )
			.addCFunction ("print_info",    l_osd_printf_info )
			.addCFunction ("print_debug",   l_osd_printf_debug )
			.beginClass <machine_manager>("manager")
				.addFunction("machine", &machine_manager::machine)
				.addFunction("options", &machine_manager::options)
			.endClass()
			.beginClass <mame_machine_manager> ("mame_manager")
				.addFunction ("plugins", &mame_machine_manager::plugins)
				.addFunction ("cheat", &mame_machine_manager::cheat)
				.addFunction ("ui", &mame_machine_manager::ui)
			.endClass ()
			.beginClass <lua_machine> ("lua_machine")
				.addCFunction ("popmessage", &lua_machine::l_popmessage)
				.addCFunction ("logerror", &lua_machine::l_logerror)
			.endClass ()
			.deriveClass <running_machine, lua_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)
				.addFunction ("video", &running_machine::video)
				.addFunction ("render", &running_machine::render)
				.addFunction ("ioport", &running_machine::ioport)
				.addFunction ("parameters", &running_machine::parameters)
				.addFunction ("memory", &running_machine::memory)
				.addFunction ("options", &running_machine::options)
				.addFunction ("outputs", &running_machine::output)
				.addFunction ("input", &running_machine::ui_input)
				.addProperty <bool> ("paused", &running_machine::paused)
				.addProperty <luabridge::LuaRef, void> ("devices", &lua_engine::l_machine_get_devices)
				.addProperty <luabridge::LuaRef, void> ("screens", &lua_engine::l_machine_get_screens)
				.addProperty <luabridge::LuaRef, void> ("images", &lua_engine::l_machine_get_images)
			.endClass ()
			.beginClass <game_driver> ("game_driver")
				.addData ("source_file", &game_driver::source_file)
				.addData ("parent", &game_driver::parent)
				.addData ("name", &game_driver::name)
				.addData ("description", &game_driver::description)
				.addData ("year", &game_driver::year)
				.addData ("manufacturer", &game_driver::manufacturer)
				.addData ("compatible_with", &game_driver::compatible_with)
				.addData ("default_layout", &game_driver::default_layout)
			.endClass ()
			.beginClass <device_t> ("device")
				.addFunction ("name", &device_t::name)
				.addFunction ("shortname", &device_t::shortname)
				.addFunction ("tag", &device_t::tag)
				.addFunction ("owner", &device_t::owner)
				.addProperty <luabridge::LuaRef, void> ("spaces", &lua_engine::l_dev_get_memspaces)
				.addProperty <luabridge::LuaRef, void> ("state", &lua_engine::l_dev_get_states)
				.addProperty <luabridge::LuaRef, void> ("items", &lua_engine::l_dev_get_items)
			.endClass()
			.beginClass <cheat_manager> ("cheat")
				.addProperty <bool, bool> ("enabled", &cheat_manager::enabled, &cheat_manager::set_enable)
				.addFunction ("reload", &cheat_manager::reload)
				.addFunction ("save_all", &cheat_manager::save_all)
				.addProperty <luabridge::LuaRef, void> ("entries", &lua_engine::l_cheat_get_entries)
			.endClass()
			.beginClass <lua_cheat_entry> ("lua_cheat_entry")
				.addCFunction ("state", &lua_cheat_entry::l_get_state)
			.endClass()
			.deriveClass <cheat_entry, lua_cheat_entry> ("cheat_entry")
				.addFunction ("description", &cheat_entry::description)
				.addFunction ("comment", &cheat_entry::comment)
				.addFunction ("has_run_script", &cheat_entry::has_run_script)
				.addFunction ("has_on_script", &cheat_entry::has_on_script)
				.addFunction ("has_off_script", &cheat_entry::has_off_script)
				.addFunction ("has_change_script", &cheat_entry::has_change_script)
				.addFunction ("execute_off_script", &cheat_entry::execute_off_script)
				.addFunction ("execute_on_script", &cheat_entry::execute_on_script)
				.addFunction ("execute_run_script", &cheat_entry::execute_run_script)
				.addFunction ("execute_change_script", &cheat_entry::execute_change_script)
				.addFunction ("is_text_only", &cheat_entry::is_text_only)
				.addFunction ("is_oneshot", &cheat_entry::is_oneshot)
				.addFunction ("is_onoff", &cheat_entry::is_onoff)
				.addFunction ("is_value_parameter", &cheat_entry::is_value_parameter)
				.addFunction ("is_itemlist_parameter", &cheat_entry::is_itemlist_parameter)
				.addFunction ("is_oneshot_parameter", &cheat_entry::is_oneshot_parameter)
				.addFunction ("activate", &cheat_entry::activate)
				.addFunction ("select_default_state", &cheat_entry::select_default_state)
				.addFunction ("select_previous_state", &cheat_entry::select_previous_state)
				.addFunction ("select_next_state", &cheat_entry::select_next_state)
			.endClass()
			.beginClass <ioport_manager> ("ioport")
				.addFunction ("has_configs", &ioport_manager::has_configs)
				.addFunction ("has_analog", &ioport_manager::has_analog)
				.addFunction ("has_dips", &ioport_manager::has_dips)
				.addFunction ("has_bioses", &ioport_manager::has_bioses)
				.addFunction ("has_keyboard", &ioport_manager::has_keyboard)
				.addFunction ("count_players", &ioport_manager::count_players)
				.addProperty <luabridge::LuaRef, void> ("ports", &lua_engine::l_ioport_get_ports)
			.endClass()
			.beginClass <ioport_port> ("ioport_port")
				.addFunction ("tag", &ioport_port::tag)
				.addFunction ("active", &ioport_port::active)
				.addFunction ("live", &ioport_port::live)
				.addFunction ("read", &ioport_port::read)
				.addFunction ("write", &ioport_port::write)
				.addFunction ("field", &ioport_port::field)
				.addProperty <luabridge::LuaRef, void> ("fields", &lua_engine::l_ioports_port_get_fields)
			.endClass()
			.beginClass <ioport_field> ("ioport_field")
				.addFunction ("set_value", &ioport_field::set_value)
				.addProperty ("device", &ioport_field::device)
				.addProperty ("name", &ioport_field::name)
				.addProperty <UINT8, UINT8> ("player", &ioport_field::player, &ioport_field::set_player)
				.addProperty ("mask", &ioport_field::mask)
				.addProperty ("defvalue", &ioport_field::defvalue)
				.addProperty ("sensitivity", &ioport_field::sensitivity)
				.addProperty ("way", &ioport_field::way)
				.addProperty ("is_analog", &ioport_field::is_analog)
				.addProperty ("is_digital_joystick", &ioport_field::is_digital_joystick)
				.addProperty ("enabled", &ioport_field::enabled)
				.addProperty ("optional", &ioport_field::optional)
				.addProperty ("cocktail", &ioport_field::cocktail)
				.addProperty ("toggle", &ioport_field::toggle)
				.addProperty ("rotated", &ioport_field::rotated)
				.addProperty ("analog_reverse", &ioport_field::analog_reverse)
				.addProperty ("analog_reset", &ioport_field::analog_reset)
				.addProperty ("analog_wraps", &ioport_field::analog_wraps)
				.addProperty ("analog_invert", &ioport_field::analog_invert)
				.addProperty ("impulse", &ioport_field::impulse)
				.addProperty ("type", &ioport_field::type)
				.addProperty <double, double> ("crosshair_scale", &ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale)
				.addProperty <double, double> ("crosshair_offset", &ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset)
			.endClass()
			.beginClass <core_options> ("core_options")
				.addFunction ("help", &core_options::output_help)
				.addFunction ("command", &core_options::command)
				.addProperty <luabridge::LuaRef, void> ("entries", &lua_engine::l_options_get_entries)
			.endClass()
			.beginClass <lua_options_entry> ("lua_options_entry")
				.addCFunction ("value", &lua_options_entry::l_entry_value)
			.endClass()
			.deriveClass <core_options::entry, lua_options_entry> ("core_options_entry")
				.addFunction ("description", &core_options::entry::description)
				.addFunction ("default_value", &core_options::entry::default_value)
				.addFunction ("minimum", &core_options::entry::minimum)
				.addFunction ("maximum", &core_options::entry::maximum)
				.addFunction ("has_range", &core_options::entry::has_range)
			.endClass()
			.deriveClass <emu_options, core_options> ("emu_options")
			.endClass()
			.deriveClass <ui_options, core_options> ("ui_options")
			.endClass()
			.deriveClass <plugin_options, core_options> ("plugin_options")
			.endClass()
			.beginClass <parameters_manager> ("parameters")
				.addFunction ("add", &parameters_manager::add)
				.addFunction ("lookup", &parameters_manager::lookup)
			.endClass()
			.beginClass <lua_video> ("lua_video_manager")
				.addCFunction ("begin_recording", &lua_video::l_begin_recording)
				.addCFunction ("end_recording", &lua_video::l_end_recording)
			.endClass()
			.deriveClass <video_manager, lua_video> ("video")
				.addFunction ("snapshot", &video_manager::save_active_screen_snapshots)
				.addFunction ("is_recording", &video_manager::is_recording)
				.addFunction ("skip_this_frame", &video_manager::skip_this_frame)
				.addFunction ("speed_factor", &video_manager::speed_factor)
				.addFunction ("speed_percent", &video_manager::speed_percent)
				.addProperty <int, int> ("frameskip", &video_manager::frameskip, &video_manager::set_frameskip)
				.addProperty <bool, bool> ("throttled", &video_manager::throttled, &video_manager::set_throttled)
				.addProperty <float, float> ("throttle_rate", &video_manager::throttle_rate, &video_manager::set_throttle_rate)
			.endClass()
			.beginClass <lua_addr_space> ("lua_addr_space")
				.addConstructor <void (*)(address_space *, device_memory_interface *)> ()
				.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>)
				.addCFunction ("read_log_i8", &lua_addr_space::l_log_mem_read<INT8>)
				.addCFunction ("read_log_u8", &lua_addr_space::l_log_mem_read<UINT8>)
				.addCFunction ("read_log_i16", &lua_addr_space::l_log_mem_read<INT16>)
				.addCFunction ("read_log_u16", &lua_addr_space::l_log_mem_read<UINT16>)
				.addCFunction ("read_log_i32", &lua_addr_space::l_log_mem_read<INT32>)
				.addCFunction ("read_log_u32", &lua_addr_space::l_log_mem_read<UINT32>)
				.addCFunction ("read_log_i64", &lua_addr_space::l_log_mem_read<INT64>)
				.addCFunction ("read_log_u64", &lua_addr_space::l_log_mem_read<UINT64>)
				.addCFunction ("write_log_i8", &lua_addr_space::l_log_mem_write<INT8>)
				.addCFunction ("write_log_u8", &lua_addr_space::l_log_mem_write<UINT8>)
				.addCFunction ("write_log_i16", &lua_addr_space::l_log_mem_write<INT16>)
				.addCFunction ("write_log_u16", &lua_addr_space::l_log_mem_write<UINT16>)
				.addCFunction ("write_log_i32", &lua_addr_space::l_log_mem_write<INT32>)
				.addCFunction ("write_log_u32", &lua_addr_space::l_log_mem_write<UINT32>)
				.addCFunction ("write_log_i64", &lua_addr_space::l_log_mem_write<INT64>)
				.addCFunction ("write_log_u64", &lua_addr_space::l_log_mem_write<UINT64>)
				.addCFunction ("read_direct_i8", &lua_addr_space::l_direct_mem_read<INT8>)
				.addCFunction ("read_direct_u8", &lua_addr_space::l_direct_mem_read<UINT8>)
				.addCFunction ("read_direct_i16", &lua_addr_space::l_direct_mem_read<INT16>)
				.addCFunction ("read_direct_u16", &lua_addr_space::l_direct_mem_read<UINT16>)
				.addCFunction ("read_direct_i32", &lua_addr_space::l_direct_mem_read<INT32>)
				.addCFunction ("read_direct_u32", &lua_addr_space::l_direct_mem_read<UINT32>)
				.addCFunction ("read_direct_i64", &lua_addr_space::l_direct_mem_read<INT64>)
				.addCFunction ("read_direct_u64", &lua_addr_space::l_direct_mem_read<UINT64>)
				.addCFunction ("write_direct_i8", &lua_addr_space::l_direct_mem_write<INT8>)
				.addCFunction ("write_direct_u8", &lua_addr_space::l_direct_mem_write<UINT8>)
				.addCFunction ("write_direct_i16", &lua_addr_space::l_direct_mem_write<INT16>)
				.addCFunction ("write_direct_u16", &lua_addr_space::l_direct_mem_write<UINT16>)
				.addCFunction ("write_direct_i32", &lua_addr_space::l_direct_mem_write<INT32>)
				.addCFunction ("write_direct_u32", &lua_addr_space::l_direct_mem_write<UINT32>)
				.addCFunction ("write_direct_i64", &lua_addr_space::l_direct_mem_write<INT64>)
				.addCFunction ("write_direct_u64", &lua_addr_space::l_direct_mem_write<UINT64>)
				.addProperty <const char *> ("name", &lua_addr_space::name)
				.addProperty <luabridge::LuaRef, void> ("map", &lua_engine::l_addr_space_map)
			.endClass()
			.beginClass <lua_ui_input> ("lua_input")
				.addCFunction ("find_mouse", &lua_ui_input::l_ui_input_find_mouse)
			.endClass()
			.deriveClass <ui_input_manager, lua_ui_input> ("input")
				.addFunction ("pressed", &ui_input_manager::pressed)
			.endClass()
			.beginClass <lua_render_target> ("lua_target")
				.addCFunction ("view_bounds", &lua_render_target::l_render_view_bounds)
			.endClass()
			.deriveClass <render_target, lua_render_target> ("target")
				.addFunction ("width", &render_target::width)
				.addFunction ("height", &render_target::height)
				.addFunction ("pixel_aspect", &render_target::pixel_aspect)
				.addFunction ("hidden", &render_target::hidden)
				.addFunction ("is_ui_target", &render_target::is_ui_target)
				.addFunction ("index", &render_target::index)
				.addProperty <float, float> ("max_update_rate", &render_target::max_update_rate, &render_target::set_max_update_rate)
				.addProperty <int, int> ("view", &render_target::view, &render_target::set_view)
				.addProperty <int, int> ("orientation", &render_target::orientation, &render_target::set_orientation)
				.addProperty <bool, bool> ("backdrops", &render_target::backdrops_enabled, &render_target::set_backdrops_enabled)
				.addProperty <bool, bool> ("overlays", &render_target::overlays_enabled, &render_target::set_overlays_enabled)
				.addProperty <bool, bool> ("bezels", &render_target::bezels_enabled, &render_target::set_bezels_enabled)
				.addProperty <bool, bool> ("marquees", &render_target::marquees_enabled, &render_target::set_marquees_enabled)
				.addProperty <bool, bool> ("screen_overlay", &render_target::screen_overlay_enabled, &render_target::set_screen_overlay_enabled)
				.addProperty <bool, bool> ("zoom", &render_target::zoom_to_screen, &render_target::set_zoom_to_screen)
			.endClass()
			.beginClass <render_container> ("render_container")
				.addFunction ("orientation", &render_container::orientation)
				.addFunction ("xscale", &render_container::xscale)
				.addFunction ("yscale", &render_container::yscale)
				.addFunction ("xoffset", &render_container::xoffset)
				.addFunction ("yoffset", &render_container::yoffset)
				.addFunction ("is_empty", &render_container::is_empty)
			.endClass()
			.beginClass <render_manager> ("render")
				.addFunction ("max_update_rate", &render_manager::max_update_rate)
				.addFunction ("ui_target", &render_manager::ui_target)
				.addFunction ("ui_container", &render_manager::ui_container)
				.addProperty <luabridge::LuaRef, void> ("targets", &lua_engine::l_render_get_targets)
			.endClass()
			.beginClass <mame_ui_manager> ("ui")
				.addFunction ("is_menu_active", &mame_ui_manager::is_menu_active)
				.addFunction ("options", &mame_ui_manager::options)
				.addProperty <bool, bool> ("show_fps", &mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps)
				.addProperty <bool, bool> ("show_profiler", &mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler)
				.addProperty <bool, bool> ("single_step", &mame_ui_manager::single_step, &mame_ui_manager::set_single_step)
				.addFunction ("get_line_height", &mame_ui_manager::get_line_height)
			.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)
				.addCFunction ("orientation", &lua_screen::l_orientation)
				.addCFunction ("refresh", &lua_screen::l_refresh)
				.addCFunction ("snapshot", &lua_screen::l_snapshot)
				.addCFunction ("type", &lua_screen::l_type)
			.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)
				.addFunction ("xscale", &screen_device::xscale)
				.addFunction ("yscale", &screen_device::yscale)
			.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()
			.beginClass <memory_manager> ("memory")
				.addProperty <luabridge::LuaRef, void> ("banks", &lua_engine::l_memory_get_banks)
				.addProperty <luabridge::LuaRef, void> ("regions", &lua_engine::l_memory_get_regions)
				.addProperty <luabridge::LuaRef, void> ("shares", &lua_engine::l_memory_get_shares)
			.endClass()
			.beginClass <lua_memory_region> ("lua_region")
				.addCFunction ("read_i8", &lua_memory_region::l_region_read<INT8>)
				.addCFunction ("read_u8", &lua_memory_region::l_region_read<UINT8>)
				.addCFunction ("read_i16", &lua_memory_region::l_region_read<INT16>)
				.addCFunction ("read_u16", &lua_memory_region::l_region_read<UINT16>)
				.addCFunction ("read_i32", &lua_memory_region::l_region_read<INT32>)
				.addCFunction ("read_u32", &lua_memory_region::l_region_read<UINT32>)
				.addCFunction ("read_i64", &lua_memory_region::l_region_read<INT64>)
				.addCFunction ("read_u64", &lua_memory_region::l_region_read<UINT64>)
				.addCFunction ("write_i8", &lua_memory_region::l_region_write<INT8>)
				.addCFunction ("write_u8", &lua_memory_region::l_region_write<UINT8>)
				.addCFunction ("write_i16", &lua_memory_region::l_region_write<INT16>)
				.addCFunction ("write_u16", &lua_memory_region::l_region_write<UINT16>)
				.addCFunction ("write_i32", &lua_memory_region::l_region_write<INT32>)
				.addCFunction ("write_u32", &lua_memory_region::l_region_write<UINT32>)
				.addCFunction ("write_i64", &lua_memory_region::l_region_write<INT64>)
				.addCFunction ("write_u64", &lua_memory_region::l_region_write<UINT64>)
			.endClass()
			.deriveClass <memory_region, lua_memory_region> ("region")
				.addProperty <UINT32> ("size", &memory_region::bytes)
			.endClass()
			.beginClass <lua_memory_share> ("lua_share")
				.addCFunction ("read_i8", &lua_memory_share::l_share_read<INT8>)
				.addCFunction ("read_u8", &lua_memory_share::l_share_read<UINT8>)
				.addCFunction ("read_i16", &lua_memory_share::l_share_read<INT16>)
				.addCFunction ("read_u16", &lua_memory_share::l_share_read<UINT16>)
				.addCFunction ("read_i32", &lua_memory_share::l_share_read<INT32>)
				.addCFunction ("read_u32", &lua_memory_share::l_share_read<UINT32>)
				.addCFunction ("read_i64", &lua_memory_share::l_share_read<INT64>)
				.addCFunction ("read_u64", &lua_memory_share::l_share_read<UINT64>)
				.addCFunction ("write_i8", &lua_memory_share::l_share_write<INT8>)
				.addCFunction ("write_u8", &lua_memory_share::l_share_write<UINT8>)
				.addCFunction ("write_i16", &lua_memory_share::l_share_write<INT16>)
				.addCFunction ("write_u16", &lua_memory_share::l_share_write<UINT16>)
				.addCFunction ("write_i32", &lua_memory_share::l_share_write<INT32>)
				.addCFunction ("write_u32", &lua_memory_share::l_share_write<UINT32>)
				.addCFunction ("write_i64", &lua_memory_share::l_share_write<INT64>)
				.addCFunction ("write_u64", &lua_memory_share::l_share_write<UINT64>)
			.endClass()
			.deriveClass <memory_share, lua_memory_share> ("region")
				.addProperty <size_t> ("size", &memory_share::bytes)
			.endClass()
			.beginClass <output_manager> ("output")
				.addFunction ("set_value", &output_manager::set_value)
				.addFunction ("set_indexed_value", &output_manager::set_indexed_value)
				.addFunction ("get_value", &output_manager::get_value)
				.addFunction ("get_indexed_value", &output_manager::get_indexed_value)
				.addFunction ("name_to_id", &output_manager::name_to_id)
				.addFunction ("id_to_name", &output_manager::id_to_name)
			.endClass()
			.beginClass <device_image_interface> ("image")
				.addFunction ("exists", &device_image_interface::exists)
				.addFunction ("filename", &device_image_interface::filename)
				.addFunction ("longname", &device_image_interface::longname)
				.addFunction ("manufacturer", &device_image_interface::manufacturer)
				.addFunction ("year", &device_image_interface::year)
				.addFunction ("software_list_name", &device_image_interface::software_list_name)
				.addFunction ("image_type_name", &device_image_interface::image_type_name)
				.addFunction ("load", &device_image_interface::load)
				.addFunction ("unload", &device_image_interface::unload)
				.addFunction ("crc", &device_image_interface::crc)
				.addProperty <const device_t &> ("device", static_cast<const device_t &(device_image_interface::*)() const>(&device_image_interface::device))
				.addProperty <bool> ("is_readable", &device_image_interface::is_readable)
				.addProperty <bool> ("is_writeable", &device_image_interface::is_writeable)
				.addProperty <bool> ("is_creatable", &device_image_interface::is_creatable)
				.addProperty <bool> ("is_reset_on_load", &device_image_interface::is_reset_on_load)
			.endClass()
			.beginClass <lua_emu_file> ("file")
				.addConstructor <void (*)(const char *, UINT32)> ()
				.addCFunction ("read", &lua_emu_file::l_emu_file_read)
				.addFunction ("open", &lua_emu_file::open)
				.addFunction ("open_next", &lua_emu_file::open_next)
				.addFunction ("seek", &lua_emu_file::seek)
				.addFunction ("size", &lua_emu_file::size)
				.addFunction ("filename", &lua_emu_file::filename)
				.addFunction ("fullpath", &lua_emu_file::fullpath)
			.endClass()
			.beginClass <lua_item> ("item")
				.addConstructor <void (*)(int)> ()
				.addData ("size", &lua_item::l_item_size, false)
				.addData ("count", &lua_item::l_item_count, false)
				.addCFunction ("read", &lua_item::l_item_read)
				.addCFunction ("read_block", &lua_item::l_item_read_block)
				.addCFunction ("write", &lua_item::l_item_write)
			.endClass()
		.endNamespace();

	luabridge::push (m_lua_state, (machine_manager*)mame_machine_manager::instance());
	lua_setglobal(m_lua_state, "manager");
	luabridge::push(m_lua_state, mame_machine_manager::instance());
	lua_setglobal(m_lua_state, "mame_manager");
}

void lua_engine::start_console()
{
	std::thread th(::serve_lua, this);
	th.detach();
}

//-------------------------------------------------
//  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 != nullptr) {
		// 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()
{
	std::lock_guard<std::mutex> lock(g_mutex);
	if (msg.ready == 1) {
		lua_settop(m_lua_state, 0);
		int status = luaL_loadbuffer(m_lua_state, msg.text.c_str(), msg.text.length(), "=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;
	}
}

//-------------------------------------------------
//  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 <unsigned long long> {
		static inline void push (lua_State* L, unsigned long long value) {
			lua_pushunsigned(L, static_cast <lua_Unsigned> (value));
		}

		static inline unsigned long long get (lua_State* L, int index) {
			return static_cast <unsigned long long> (luaL_checkunsigned (L, index));
		}
	};
}