View Page Source

Revision (current)
Last Updated by feos on 8/4/2022 1:52 PM
Back to Page


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:
%%SRC_EMBED lua
local JoypadUp = {up=true}
local function HoldUp()
  Joypad.set(1,JoypadUp)
end
while true do
  HoldUp()
  emu.frameadvance()
end
%%END_EMBED

!!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:
%%SRC_EMBED lua
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)
%%END_EMBED

!!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:
%%SRC_EMBED lua
local function Fn()
  --Insert really awesome code here
end
emu.registerbefore(Fn)
%%END_EMBED

!!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:
%%SRC_EMBED lua
local function Fn()
  --Insert somewhat mediocre code here
end
emu.registerafter(Fn)
%%END_EMBED

----

!!!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 [ReverseEngineering|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.

* Good example
%%SRC_EMBED lua
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
%%END_EMBED

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.

* Bad example 1 (don't do it!)
%%SRC_EMBED lua
local function SomeFunction()
  -- Insert code, as always
end
emu.registerbefore(SomeFunction())
%%END_EMBED

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.

* Bad example 2 (don't do this, either!)
%%SRC_EMBED lua
local function ThisIsAJoke()
  -- Be sure to throw an error for extra irritation!
end
while true do
  emu.registerbefore(ThisIsAJoke)
  emu.frameadvance()
end
%%END_EMBED

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:
* Immediate: The data is transferred without any frame delay. The best possible case.
* Late: If {{set}}, it applies the input next frame. If {{get}}, it returns a table with input from the last frame. Either way, there's an unwanted 1 frame delay.
* Ignored: Nothing you place in {{joypad.set}} will be picked up by the emulator. Useless register for botting.

||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|
----
[user: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.