From 017f4e485a5c0acfd72f522d4909f19616ff879d Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Thu, 25 Dec 2014 19:10:11 +0100 Subject: [PATCH 1/4] luaengine: add screen drawing/HUD capabilities This commit allows LUA scripts to implement HUD capabilities, by overlaying elements (lines, boxes, text) to screen. Mostly used to draw custom graphic helpers for trainings and TAS runs development. Signed-off-by: Luca Bruno --- src/emu/luaengine.c | 136 +++++++++++++++++++++++++++++++++++++++++++- src/emu/luaengine.h | 6 ++ 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/emu/luaengine.c b/src/emu/luaengine.c index 843c873233e..712da413520 100644 --- a/src/emu/luaengine.c +++ b/src/emu/luaengine.c @@ -17,6 +17,7 @@ #include "emuopts.h" #include "osdepend.h" #include "drivenum.h" +#include "ui/ui.h" #include "web/mongoose.h" //************************************************************************** @@ -336,6 +337,26 @@ int lua_engine::l_emu_hook_output(lua_State *L) return 0; } +//------------------------------------------------- +// machine_get_screens - return table of available screens userdata +// -> manager:machine().screens[":screen"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_machine_get_screens(const running_machine *r) +{ + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef screens_table = luabridge::LuaRef::newTable(L); + + for (device_t *dev = r->first_screen(); dev != NULL; dev = dev->next()) { + screen_device *sc = dynamic_cast(dev); + if (sc && sc->configured() && sc->started() && sc->type()) { + screens_table[sc->tag()] = sc; + } + } + + return screens_table; +} + //------------------------------------------------- // machine_get_devices - return table of available devices userdata // -> manager:machine().devices[":maincpu"] @@ -438,6 +459,108 @@ int lua_engine::lua_addr_space::l_mem_read(lua_State *L) } +//------------------------------------------------- +// draw_box - draw a box on a screen container +// -> manager:machine().screens[":screen"]:draw_box(x1, y1, x2, y2, bgcolor, linecolor) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_box(lua_State *L) +{ + screen_device *sc = luabridge::Stack::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got 6 numerical parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 6), 6, "background color (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 7), 7, "outline color (integer) expected"); + + // retrieve all parameters + float x1, y1, x2, y2; + x1 = MIN(lua_tounsigned(L, 2) / static_cast(sc->width()) , 1.0f); + y1 = MIN(lua_tounsigned(L, 3) / static_cast(sc->height()), 1.0f); + x2 = MIN(lua_tounsigned(L, 4) / static_cast(sc->width()) , 1.0f); + y2 = MIN(lua_tounsigned(L, 5) / static_cast(sc->height()), 1.0f); + UINT32 bgcolor = lua_tounsigned(L, 6); + UINT32 fgcolor = lua_tounsigned(L, 7); + + // draw the box + render_container &rc = sc->container(); + ui_manager &ui = sc->machine().ui(); + ui.draw_outlined_box(&rc, x1, y1, x2, y2, fgcolor, bgcolor); + + return 0; +} + +//------------------------------------------------- +// draw_line - draw a line on a screen container +// -> manager:machine().screens[":screen"]:draw_line(x1, y1, x2, y2, linecolor) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_line(lua_State *L) +{ + screen_device *sc = luabridge::Stack::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got 5 numerical parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 6), 6, "color (integer) expected"); + + // retrieve all parameters + float x1, y1, x2, y2; + x1 = MIN(lua_tounsigned(L, 2) / static_cast(sc->width()) , 1.0f); + y1 = MIN(lua_tounsigned(L, 3) / static_cast(sc->height()), 1.0f); + x2 = MIN(lua_tounsigned(L, 4) / static_cast(sc->width()) , 1.0f); + y2 = MIN(lua_tounsigned(L, 5) / static_cast(sc->height()), 1.0f); + UINT32 color = lua_tounsigned(L, 6); + + // draw the line + sc->container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + return 0; +} + +//------------------------------------------------- +// draw_text - draw text on a screen container +// -> manager:machine().screens[":screen"]:draw_text(x, y, message) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_text(lua_State *L) +{ + screen_device *sc = luabridge::Stack::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got proper parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y (integer) expected"); + luaL_argcheck(L, lua_isstring(L, 4), 4, "message (string) expected"); + + // retrieve all parameters + float x = MIN(lua_tounsigned(L, 2) / static_cast(sc->width()) , 1.0f); + float y = MIN(lua_tounsigned(L, 3) / static_cast(sc->height()), 1.0f); + const char *msg = luaL_checkstring(L,4); + // TODO: add optional parameters (colors, etc.) + + // draw the text + render_container &rc = sc->container(); + ui_manager &ui = sc->machine().ui(); + ui.draw_text_full(&rc, msg, x, y , (1.0f - x), + JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, + UI_TEXT_BG_COLOR, NULL, NULL); + + return 0; +} + void *lua_engine::checkparam(lua_State *L, int idx, const char *tname) { const char *name; @@ -667,6 +790,7 @@ void lua_engine::initialize() .addFunction ("soft_reset", &running_machine::schedule_soft_reset) .addFunction ("system", &running_machine::system) .addProperty ("devices", &lua_engine::l_machine_get_devices) + .addProperty ("screens", &lua_engine::l_machine_get_screens) .endClass () .beginClass ("game_driver") .addData ("name", &game_driver::name) @@ -691,7 +815,17 @@ void lua_engine::initialize() .deriveClass ("addr_space") .addFunction("name", &address_space::name) .endClass() - .endNamespace (); + .beginClass ("lua_screen_dev") + .addCFunction ("draw_box", &lua_screen::l_draw_box) + .addCFunction ("draw_line", &lua_screen::l_draw_line) + .addCFunction ("draw_text", &lua_screen::l_draw_text) + .endClass() + .deriveClass ("screen_dev") + .addFunction ("name", &screen_device::name) + .addFunction ("height", &screen_device::height) + .addFunction ("width", &screen_device::width) + .endClass() + .endNamespace(); luabridge::push (m_lua_state, machine_manager::instance()); lua_setglobal(m_lua_state, "manager"); diff --git a/src/emu/luaengine.h b/src/emu/luaengine.h index ae1221415ff..d2f7d9323f1 100644 --- a/src/emu/luaengine.h +++ b/src/emu/luaengine.h @@ -105,6 +105,12 @@ private: struct lua_addr_space { template int l_mem_read(lua_State *L); }; + static luabridge::LuaRef l_machine_get_screens(const running_machine *r); + struct lua_screen { + int l_draw_box(lua_State *L); + int l_draw_line(lua_State *L); + int l_draw_text(lua_State *L); + }; void resume(void *L, INT32 param); void report_errors(int status); From be2e7b57ebc3c96103c829aa6371fb70690b9f74 Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Thu, 25 Dec 2014 19:25:09 +0100 Subject: [PATCH 2/4] luaengine: add frame hooking support This commit adds a method to let LUA scripts register a callback to be invoked before rendering each frame. This callback typically makes use of screen drawing methods to draw a custom HUD on top of each frame. Signed-off-by: Luca Bruno --- src/emu/luaengine.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/emu/luaengine.h | 5 +++++ src/emu/video.c | 3 +++ 3 files changed, 51 insertions(+) diff --git a/src/emu/luaengine.c b/src/emu/luaengine.c index 712da413520..9bf22d6db12 100644 --- a/src/emu/luaengine.c +++ b/src/emu/luaengine.c @@ -337,6 +337,31 @@ int lua_engine::l_emu_hook_output(lua_State *L) return 0; } +int lua_engine::l_emu_set_hook(lua_State *L) +{ + luaThis->emu_set_hook(L); + return 0; +} + +void lua_engine::emu_set_hook(lua_State *L) +{ + luaL_argcheck(L, lua_isfunction(L, 1) || lua_isnil(L, 1), 1, "callback function expected"); + luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected"); + const char *hookname = luaL_checkstring(L,2); + + if (strcmp(hookname, "output") == 0) { + hook_output_cb.set(L, 1); + if (!output_notifier_set) { + output_set_notifier(NULL, s_output_notifier, this); + output_notifier_set = true; + } + } else if (strcmp(hookname, "frame") == 0) { + hook_frame_cb.set(L, 1); + } else { + luai_writestringerror("%s", "Unknown hook name, aborting.\n"); + } +} + //------------------------------------------------- // machine_get_screens - return table of available screens userdata // -> manager:machine().screens[":screen"] @@ -773,6 +798,7 @@ void lua_engine::initialize() .addCFunction ("romname", l_emu_romname ) .addCFunction ("keypost", l_emu_keypost ) .addCFunction ("hook_output", l_emu_hook_output ) + .addCFunction ("sethook", l_emu_set_hook ) .addCFunction ("time", l_emu_time ) .addCFunction ("wait", l_emu_wait ) .addCFunction ("after", l_emu_after ) @@ -836,6 +862,23 @@ void lua_engine::start_console() mg_start_thread(::serve_lua, this); } +//------------------------------------------------- +// frame_hook - called at each frame refresh, used to draw a HUD +//------------------------------------------------- +bool lua_engine::frame_hook() +{ + bool is_cb_hooked = false; + if (m_machine != NULL) { + // invoke registered callback (if any) + is_cb_hooked = hook_frame_cb.active(); + if (is_cb_hooked) { + lua_State *L = hook_frame_cb.precall(); + hook_frame_cb.call(this, L, 0); + } + } + return is_cb_hooked; +} + void lua_engine::periodic_check() { osd_lock_acquire(lock); diff --git a/src/emu/luaengine.h b/src/emu/luaengine.h index d2f7d9323f1..0f5fcdda2b0 100644 --- a/src/emu/luaengine.h +++ b/src/emu/luaengine.h @@ -44,6 +44,7 @@ public: void serve_lua(); void periodic_check(); + bool frame_hook(); void resume(lua_State *L, int nparam = 0, lua_State *root = NULL); void set_machine(running_machine *machine) { m_machine = machine; update_machine(); } @@ -68,6 +69,8 @@ private: hook hook_output_cb; bool output_notifier_set; + hook hook_frame_cb; + static lua_engine* luaThis; std::map > thread_registry; @@ -82,6 +85,7 @@ private: int emu_after(lua_State *L); int emu_wait(lua_State *L); void emu_hook_output(lua_State *L); + void emu_set_hook(lua_State *L); static int l_ioport_write(lua_State *L); static int l_emu_after(lua_State *L); @@ -97,6 +101,7 @@ private: static int l_emu_start(lua_State *L); static int l_emu_pause(lua_State *L); static int l_emu_unpause(lua_State *L); + static int l_emu_set_hook(lua_State *L); // "emu.machine" namespace static luabridge::LuaRef l_machine_get_devices(const running_machine *r); diff --git a/src/emu/video.c b/src/emu/video.c index 63c8e6d4b80..2cc4ff225f2 100644 --- a/src/emu/video.c +++ b/src/emu/video.c @@ -655,6 +655,9 @@ bool video_manager::finish_screen_updates() if (screen->update_quads()) anything_changed = true; + // draw HUD from LUA callback (if any) + anything_changed |= machine().manager().lua()->frame_hook(); + // update our movie recording and burn-in state if (!machine().paused()) { From 3c8124c88de63ccd263c83b5b381dc4ec1e07b08 Mon Sep 17 00:00:00 2001 From: mamehaze Date: Mon, 5 Jan 2015 18:05:03 +0000 Subject: [PATCH 3/4] caveman (pinball) - simple code to draw the video screen (nw) --- src/mame/drivers/gts80a.c | 55 +++++++++- src/mame/layout/gts80a_caveman.lay | 159 +++++++++++++++++++++++++++++ src/mame/mame.mak | 3 +- 3 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 src/mame/layout/gts80a_caveman.lay diff --git a/src/mame/drivers/gts80a.c b/src/mame/drivers/gts80a.c index 6d10197e621..ae9bd7ca49c 100644 --- a/src/mame/drivers/gts80a.c +++ b/src/mame/drivers/gts80a.c @@ -15,6 +15,7 @@ #include "audio/gottlieb.h" #include "cpu/i86/i86.h" #include "gts80a.lh" +#include "gts80a_caveman.lh" class gts80a_state : public genpin_class { @@ -384,25 +385,75 @@ public: caveman_state(const machine_config &mconfig, device_type type, const char *tag) : gts80a_state(mconfig, type, tag) , m_videocpu(*this, "video_cpu") + , m_vram(*this, "vram") + { } + + UINT32 screen_update_caveman(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + private: required_device m_videocpu; + required_shared_ptr m_vram; }; +UINT32 caveman_state::screen_update_caveman(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + int count = 0; + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x += 4) + { + UINT8 pix = m_vram[count]; + + bitmap.pix16(y, x+0) = (pix >> 6)&0x3; + bitmap.pix16(y, x+1) = (pix >> 4)&0x3; + bitmap.pix16(y, x+2) = (pix >> 2)&0x3; + bitmap.pix16(y, x+3) = (pix >> 0)&0x3; + + count++; + } + } + + return 0; +} + + static ADDRESS_MAP_START( video_map, AS_PROGRAM, 8, caveman_state ) ADDRESS_MAP_GLOBAL_MASK(0xffff) - AM_RANGE(0x0000, 0x5fff) AM_RAM + AM_RANGE(0x0000, 0x07ff) AM_RAM + AM_RANGE(0x2000, 0x5fff) AM_RAM AM_SHARE("vram") AM_RANGE(0x8000, 0xffff) AM_ROM ADDRESS_MAP_END static ADDRESS_MAP_START( video_io_map, AS_IO, 8, caveman_state ) +// AM_RANGE(0x000, 0x002) AM_READWRITE() // 8259 irq controller +// AM_RANGE(0x100, 0x102) AM_READWRITE() // HD46505 +// AM_RANGE(0x200, 0x200) AM_READWRITE() // 8212 in, ?? out +// AM_RANGE(0x300, 0x300) AM_READWRITE() // soundlatch (command?) in, ?? out + +// AM_RANGE(0x400, 0x400) AM_READ() // joystick inputs +// AM_RANGE(0x500, 0x506) AM_WRITE() // palette + ADDRESS_MAP_END -static MACHINE_CONFIG_DERIVED( caveman, gts80a_ss ) +static MACHINE_CONFIG_DERIVED_CLASS( caveman, gts80a_ss, caveman_state ) MCFG_CPU_ADD("video_cpu", I8088, 5000000) MCFG_CPU_PROGRAM_MAP(video_map) MCFG_CPU_IO_MAP(video_io_map) + + MCFG_SCREEN_ADD("screen", RASTER) + MCFG_SCREEN_REFRESH_RATE(60) + MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0)) + MCFG_SCREEN_SIZE(256, 256) + MCFG_SCREEN_VISIBLE_AREA(0, 256-1, 0, 248-1) + MCFG_SCREEN_UPDATE_DRIVER(caveman_state, screen_update_caveman) + MCFG_SCREEN_PALETTE("palette") + + MCFG_PALETTE_ADD("palette", 16) + + MCFG_DEFAULT_LAYOUT(layout_gts80a_caveman) + MACHINE_CONFIG_END static INPUT_PORTS_START( caveman ) diff --git a/src/mame/layout/gts80a_caveman.lay b/src/mame/layout/gts80a_caveman.lay new file mode 100644 index 00000000000..cf5950d2c24 --- /dev/null +++ b/src/mame/layout/gts80a_caveman.lay @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.mak b/src/mame/mame.mak index 74247ca6b2d..7bf393a5727 100644 --- a/src/mame/mame.mak +++ b/src/mame/mame.mak @@ -2661,7 +2661,8 @@ $(DRIVERS)/goldnpkr.o: $(LAYOUT)/goldnpkr.lh \ $(DRIVERS)/gts1.o: $(LAYOUT)/gts1.lh $(DRIVERS)/gts3.o: $(LAYOUT)/gts3.lh $(DRIVERS)/gts80.o: $(LAYOUT)/gts80.lh -$(DRIVERS)/gts80a.o: $(LAYOUT)/gts80a.lh +$(DRIVERS)/gts80a.o: $(LAYOUT)/gts80a.lh \ + $(LAYOUT)/gts80a_caveman.lh $(DRIVERS)/gts80b.o: $(LAYOUT)/gts80b.lh $(DRIVERS)/lbeach.o: $(LAYOUT)/lbeach.lh From 559a09fd9ec9f4d9ee1c6a67b4142ce4df5223ed Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 5 Jan 2015 19:12:43 +0100 Subject: [PATCH 4/4] just for cross (nw) --- src/osd/windows/windows.mak | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/osd/windows/windows.mak b/src/osd/windows/windows.mak index ed1c6f47a2d..3bf63d81e5c 100644 --- a/src/osd/windows/windows.mak +++ b/src/osd/windows/windows.mak @@ -302,7 +302,11 @@ CCOMFLAGS += -include $(WINSRC)/winprefix.h include $(SRC)/build/cc_detection.mak # ensure we statically link the gcc runtime lib -LDFLAGS += -static-libgcc -static +LDFLAGS += -static-libgcc + +ifeq ($(CROSS_BUILD),1) + LDFLAGS += -static +endif # TODO: needs to use $(CC) TEST_GCC := $(shell gcc --version)