TASVideos

Tool-assisted game movies
When human skills are just not enough

Lua Scripting / Registers

<< Lua Scripting

In order to run any lua code in emulators that support it, you need to know what functions to call.

General registers

These registers are typically called once per frame, or otherwise repeatedly called without any special condition needed to trigger them.

emu.frameadvance() -- "Boundary"

Not actually a register. Typically, you stick emu.frameadvance() in a while true do ... end loop. When called, Lua releases control back to the emulator so that it can advance a frame or whatever it likes. This does not force the emulator to advance the frame, as the user still has full control over the emulator speed, and can still frame-step as desired.

For bots, this is one of the two good places to use. All emulators should accept joypad.set here, and usually without any frame delay.

For the time being, do not use this function in DeSmuME. It is glitched: While the frame counter will increment, DeSmuME will fail to emulate the frame, and nothing will happen. Other than a changed frame counter and likely desyncs for movie making.

Sample use:

   local JoypadUp = {up=true}
   local function HoldUp()
     Joypad.set(1,JoypadUp)
   end
   while true do
     HoldUp()
     emu.frameadvance()
   end

gui.register(function)

The registered function is called whenever the emulator updates its display. For all emulators, there are no guarantees that the function is called every frame. If you wish to create a bot to play parts of the game for you, this is a bad register to use, as some frames might be skipped without the bot ever knowing it happened. However, it is recommended to put most or all display-related and user-input code in this register.

Some emulators continuously updates their display even when the emulation is paused, thus calling such a registered function repeatedly. This is a very useful effect, as such scripts can react to a function like input.get(), and through that, run code based on what the user presses, without requiring the user to advance a frame.

Sample use:

   local function ShowInputKeys()
     local y = 0
     local keys= input.get()
     for k,v in pairs(keys) do
       y = y + 8
       gui.text(2,y,k)
     end
   end
   gui.register(ShowInputKeys)

emu.registerbefore(function)

The registered function should be called after the user hits the frame advance key, when the emulator updates a few internal variables, but before the actual emulation of the game.

Whether joypad.set has no delay or is even used is inconsistent across emulators, however.

If you wish to create a bot for use in DeSmuME, it is recommended to use this register.

Sample use:

   local function Fn()
     --Insert really awesome code here
   end
   emu.registerbefore(Fn)

emu.registerafter(function)

The registered function should be called immediately after the frame is emulated. Like with emu.registerbefore, there are a few noted inconsistencies across emulators.

Sample use:

   local function Fn()
     --Insert somewhat mediocre code here
   end
   emu.registerafter(Fn)

Specialized registers

From here, the use of registers are more specialized. Functions called from these registers aren't necessarily from frame advancing, but rather due to other triggers. These registers are for more advanced coders to use.

savestate.registerload(function)

The registered function is called whenever the user loads a state.

This function will be passed any values stored in the savestate that was returned by a function registered with savestate.registersave. However, there is no requirement to have a function in savestate.registersave in order to have this function work.

Useful in restoring some difficult-to-reproduce information stored by savestate.registersave. Even without that, it's also useful in detecting when the user decides to load a state.

savestate.registersave(function)

The registered function is called whenever the user saves a state.

If this function returns any values, these values are stored along with the savestate. These values are passed to the registerload function.

Typically useful if there's any important information about the current state for your lua code, that you can't easily fetch from the game's memory.

memory.register(int address,{ int size,} function)

memory.registerwrite(int address,{ int size,} function)

Whenever the selected memory location is written to, the emulator immediately calls the registered function, halting emulation until the function returns. Optionally, you can insert a number for the size, triggering the function on a range of addresses instead of just one. When called, the function is passed the address and size of what it's registered to.

Be warned: Even if the source of the change is your lua code, it will trigger this register. Ensure that the function registered within doesn't end up calling itself endlessly, either by wrapping an if statement around the related memory function or have the function de-register itself before it makes the change.

memory.registerexec(int address,{ int size,} function)

memory.registerrun(int address,{ int size,} function)

memory.registerexecute(int address,{ int size,} function)

When the emulator executes the selected memory location, it immediately calls the registered function, halting emulation until the function returns. Otherwise quite similar to the above memory.register.

Most likely, if you're using these functions, you've already looked into the debugger, or otherwise viewed the individual instructions of the game. If not, these functions will rarely, if ever, be of use. Good if you want to detect when a particular routine takes place.


How to use registers

A few things you might want to know about how to use the registers. It's quite easy to use registering functions wrong.

   local function AwesomeFn()
     --Be sure to insert awesome code here!
   end
   emu.registerbefore(AwesomeFn)
   while true do
     --Insert even more awesome code here.
     emu.frameadvance()
   end
It's perfectly fine to have the registering function outside of the frameadvance loop, and in fact is highly recommended to do so. Furthermore, you can have one registered function doing one thing and the frameadvance loop do another.

   local function SomeFunction()
     -- Insert code, as always
   end
   emu.registerbefore(SomeFunction())
By sticking SomeFunction() inside, it calls the function, then registers whatever it returns. Effectively, you did not register the function at all! If you used SomeFunction instead (note lack of parentheses), it gives the function itself to the register. This is good.

   local function ThisIsAJoke()
     -- Be sure to throw an error for extra irritation!
   end
   while true do
     emu.registerbefore(ThisIsAJoke)
     emu.frameadvance()
   end
The registering functions should be called once and only once, in many cases. By placing a registering function in a loop, you end up calling the registering function far more than once. Bad idea. Especially if the registered function throws an error. It is irritating to deal with the flood of error messages as the emulator runs, when one produces erroneous code in the registered function.


Reference tables

For the joypads:

FCEUX
2.1.5
"Boundary" Before After Gui
Joypad.set Immediate Late Late Late
Joypad.get Late Immediate Immediate Immediate

Snes9x
1.51 v7
"Boundary" Before After Gui
Joypad.set Immediate Ignored Ignored Ignored
Joypad.get Immediate Immediate Immediate Immediate

VBA
v23.5
"Boundary" Before After Gui
Joypad.set Late Ignored Ignored Ignored
Joypad.get Immediate Immediate Immediate Immediate

DeSmuME
0.9.6
"Boundary" Before After Gui
Joypad.set ??? Immediate Late Immediate
Joypad.get ??? Late Immediate Immediate

FatRatKnight: I only know about FCEUX, Snes9x, VBA, and an older version of DeSmuME. Someone else will have to fill us in on the other emulators and recent happenings on DeSmuME.


Combined RSS Feed
LuaScripting/Registers last edited by feos on 2013-09-05 03:53:38
Page info and history | Latest diff | List referrers | View Source