| Previous | 199869 Revisions | Next |
| r36597 Monday 16th March, 2015 at 11:03:09 UTC by Luca Bruno |
|---|
| docs: add initial documentation for luaengine API This commits add some initial documentation for current API exposed via luaengine. This doc is meant to be a quick walkthrough for script writers coming from mame-rr. Full methods reference is not yet included, as the API is still rapidly changing. Signed-off-by: Luca Bruno <lucab@debian.org> |
| [docs] | luaengine.md* |
| r0 | r245109 | |
|---|---|---|
| 1 | # Scripting MAME via LUA | |
| 2 | ||
| 3 | ## Introduction | |
| 4 | ||
| 5 | It is now possible to externally drive MAME via LUA scripts. | |
| 6 | This feature initially appeared in version 0.148, when a minimal `luaengine` | |
| 7 | was implemented. Nowadays, the LUA interface is rich enough | |
| 8 | to let you inspect and manipulate devices state, access CPU | |
| 9 | registers, read and write memory, and draw a custom HUD on screen. | |
| 10 | ||
| 11 | Internally, MAME makes extensive use of `luabridge` to implement | |
| 12 | this feature: the idea is to transparently expose as many of | |
| 13 | the useful internals as possible. | |
| 14 | ||
| 15 | Finally, a warning: LUA API is not yet declared stable and may | |
| 16 | suddenly change without prior notice. | |
| 17 | However, we expose methods to let you know at runtime which API | |
| 18 | version you are running against, and you can introspect most of the | |
| 19 | objects at runtime. | |
| 20 | ||
| 21 | ## Features | |
| 22 | ||
| 23 | The API is not yet complete, but this is a partial list of capabilities | |
| 24 | currently available to LUA scripts: | |
| 25 | ||
| 26 | * machine metadata (app version, current rom, rom details) | |
| 27 | * machine control (starting, pausing, resetting, stopping) | |
| 28 | * machine hooks (on frame painting and on user events) | |
| 29 | * devices introspection (device tree listing, memory and register enumeration) | |
| 30 | * screens introspection (screens listing, screen details, frames counting) | |
| 31 | * screen HUD drawing (text, lines, boxes on multiple screens) | |
| 32 | * memory read/write (8/16/32/64 bits, signed and unsigned) | |
| 33 | * registers and states control (states enumeration, get and set) | |
| 34 | ||
| 35 | ## Usage | |
| 36 | ||
| 37 | MAME supports external scripting via LUA (>= 5.3) scripts, either | |
| 38 | written on the interactive console or loaded as a file. | |
| 39 | To reach the console, just run MAME with `-console`; you will be | |
| 40 | greeted by a naked `>` prompt where you can input your script. | |
| 41 | ||
| 42 | To load a whole script at once, store it in a plaintext file and | |
| 43 | pass it via the `-autoboot_script`. Please note that script | |
| 44 | loading may be delayed (few seconds by default), but you can | |
| 45 | override the default with the `-autoboot_delay` argument. | |
| 46 | ||
| 47 | To control the execution of your code, you can use a loop-based or | |
| 48 | an event-based approach. The former is not encouraged as it is | |
| 49 | resource-intensive and makes control flow unnecessarily complex. | |
| 50 | Instead, we suggest to register custom hooks to be invoked on specific | |
| 51 | events (eg. at each frame rendering). | |
| 52 | ||
| 53 | ## Walktrough | |
| 54 | ||
| 55 | Let's first run MAME in a terminal to reach the LUA console: | |
| 56 | ``` | |
| 57 | $ mame -console YOUR_ROM | |
| 58 | M.A.M.E. v0.158 (Feb 5 2015) - Multiple Arcade Machine Emulator | |
| 59 | Copyright Nicola Salmoria and the MAME team | |
| 60 | Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio | |
| 61 | ||
| 62 | > | |
| 63 | ``` | |
| 64 | ||
| 65 | At this point, your game is probably running in demo mode, let's pause it: | |
| 66 | ``` | |
| 67 | > emu.pause() | |
| 68 | > | |
| 69 | ``` | |
| 70 | Even without textual feedback on the console, you'll notice the game is now paused. | |
| 71 | In general, commands are quiet and only print back error messages. | |
| 72 | ||
| 73 | You can check at runtime which version of MAME you are running, with: | |
| 74 | ``` | |
| 75 | > print(emu.app_name() .. " " .. emu.app_version()) | |
| 76 | mame 0.158 | |
| 77 | ``` | |
| 78 | ||
| 79 | We now start exploring screen related methods. First, let's enumerate available screens: | |
| 80 | ``` | |
| 81 | > for i,v in pairs(manager:machine().screens) do print(i) end | |
| 82 | :screen | |
| 83 | ``` | |
| 84 | ||
| 85 | `manager:machine()` is the root object of your currently running machine: | |
| 86 | we will be using this often. `screens` is a table with all available screens; | |
| 87 | most machines only have one main screen. | |
| 88 | In our case, the main and only screen is tagged as `:screen`, and we can further | |
| 89 | inspect it: | |
| 90 | ``` | |
| 91 | > -- let's define a shorthand for the main screen | |
| 92 | > s = manager:machine().screens[":screen"] | |
| 93 | > print(s:width() .. "x" .. s:height()) | |
| 94 | 320x224 | |
| 95 | ``` | |
| 96 | ||
| 97 | We have several methods to draw on the screen a HUD composed of lines, boxes and text: | |
| 98 | ``` | |
| 99 | > -- we define a HUD-drawing function, and then call it | |
| 100 | > function draw_hud() | |
| 101 | >> s:draw_text(40, 40, "foo"); -- (x0, y0, msg) | |
| 102 | >> s:draw_box(20, 20, 80, 80, 0, 0xff00ffff); -- (x0, y0, x1, y1, fill-color, line-color) | |
| 103 | >> s:draw_line(20, 20, 80, 80, 0xff00ffff); -- (x0, y0, x1, y1, line-color) | |
| 104 | >> end | |
| 105 | > draw_hud(); | |
| 106 | ``` | |
| 107 | ||
| 108 | This will draw some useless art on the screen. However, when unpausing the game, your HUD | |
| 109 | needs to be refreshed otherwise it will just disappear. In order to do this, you have to register | |
| 110 | your hook to be called on every frame repaint: | |
| 111 | ``` | |
| 112 | > emu.sethook(draw_hud, "frame") | |
| 113 | ``` | |
| 114 | ||
| 115 | Similarly to screens, you can inspect all the devices attached to a | |
| 116 | machine: | |
| 117 | ``` | |
| 118 | > for k,v in pairs(manager:machine().devices) do print(k) end | |
| 119 | :audiocpu | |
| 120 | :maincpu | |
| 121 | :saveram | |
| 122 | :screen | |
| 123 | :palette | |
| 124 | [...] | |
| 125 | ``` | |
| 126 | ||
| 127 | On some of them, you can also inspect and manipulate memory and state: | |
| 128 | ``` | |
| 129 | > cpu = manager:machine().devices[":maincpu"] | |
| 130 | > -- enumerate, read and write state registers | |
| 131 | > for k,v in pairs(cpu.state) do print(k) end | |
| 132 | D5 | |
| 133 | SP | |
| 134 | A4 | |
| 135 | A3 | |
| 136 | D0 | |
| 137 | PC | |
| 138 | [...] | |
| 139 | > print(cpu.state["D0"].value) | |
| 140 | 303 | |
| 141 | > cpu.state["D0"].value = 255 | |
| 142 | > print(cpu.state["D0"].value) | |
| 143 | 255 | |
| 144 | ``` | |
| 145 | ||
| 146 | ``` | |
| 147 | > -- inspect memory | |
| 148 | > for k,v in pairs(cpu.spaces) do print(k) end | |
| 149 | program | |
| 150 | > mem = cpu.spaces["program"] | |
| 151 | > print(mem:read_i8(0xC000)) | |
| 152 | 41 | |
| 153 | ``` | |
| 154 |
| Previous | 199869 Revisions | Next |