This is the API from DeHackEd's version of Lua in ZSnes. At the time of writing, FCUE's Lua was based on this API.
Basics
Your code will be run alongside the emulator's main loop. You code should
probably look roughly like this:
-- initialization goes here
while condition do
-- Code executed once per frame
snes9x.frameadvance()
end
-- Cleanup goes here
When Lua execution starts, the emulator will be automatically unpaused if it
is currently paused. If so, it will automatically be paused when the script
exits, voluntarily or otherwise. This allows you to have a script execute
some work on your behalf and then when it exits the emulator will be paused,
ready for the player to continue use.
Base library
Handy little things that are not put into a class. Mostly binary operations right now.
int AND(int arg1, int arg2, ..., int argn)
Since Lua lacks binary operators and since binary will come up with memory manipulation,
I offer this function. Output is the binary AND of all its parameters together.
Minimum 1 argument, all integers.
At a binary level, the AND of two binary bits is 1 if both inputs are 1, and the output
is 0 in any other case. Commonly used to test if a bit is set by ANDing with a number
with only the desired position set to 1.
int OR(int arg1, int arg2, ..., int argn)
The OR of two bits is 1 if either of the inputs is 1, and 0 if both inputs are 0.
Typically used to force a single bit to 1, regardless of its current state.
int XOR(int arg1, int arg2, ..., int argn)
XOR flips bits. An even number of 1s yields a zero and an odd number of 1s yields a 1.
Commonly used to toggle a bit by XORing.
int BIT(int which)
Returns a number with only the given bit set. which is in the range from 0 to 15 since the
SNES is a 16 bit system. BIT(15) == 32768
... Actually this system will accept a range of 0 to 30, but none of the memory access functions will
accept it, so you're on your own for those. 31 is not allowed for now due to signedness risking wreaking havoc.
Basic master emulator control.
snes9x.speedmode(string mode)
Selects the speed mode snes9x should run at while Lua is in control of frame advance. It must be set to one of the following:
In modes other than normal, pause will have no effect.
snes9x.frameadvance()
Snes9x executes one frame. This function pauses until the execution finishes. General system slowdown
when running at normal speed (ie. sleeping for 1/60 seconds) also occurs here when not in high speed mode.
Warning: Due to the way the code is written, the times this function may be called is restricted. Norably,
it must not be called within a coroutine or under a [x]pcall(). You can use coroutines for
your own purposes, but they must not call this function themselves. Furthermore, this function cannot be called from any
"registered" callback function. An error will occur if you do.
snes9x.message(string msg)
Displays the indicated string on the user's screen. snes9x.speedmode("normal") is probably the only way this is of any use,
lest the message not be displayed at all
snes9x.pause()
v0.05+ only
Pauses the emulator. This function blocks until the user unpauses.
This function is allowed to be called from outside a frame boundary (ie. when it is not allowed to call
snes9x.frameadvance). In this case, the function does not wait for the pause because you can't pause
midway through a frame. Your code will continue to execute and the emulator will be paused at the end
of the current frame. If you are at a frame boundary, this function acts a lot like snes9x.frameadvance()
plus the whole pause thing.
It might be smart to reset the speed mode to "normal" if it is not already so.
snes9x.wait()
v0.06+ only
Skips emulation of the next frame. If your script needs to wait for something to happen before proceeding (eg.
input from another application) then you should call this. Otherwise the GUI might jam up and your
application will not appear to be responding and need termination. It is expected that this function
will pause the script for 1/60 of a second without actually running the emulator itself, though it tends to be
OS-dependent right now.
If you're not sufficiently confused yet, think of this as pausing for one frame.
If you need to do a large amount of calculations -- so much that you risk setting off the rampant script
warning, just call this function every once in a while.
Might want to avoid using this if you don't need to. If the emulator is running at normal speed, paused
and the user presses frame-advance, they might be confused when nothing happens.
Memory access and manipulation.
int memory.readbyte(int address)
int memory.readword(int address)
Reads a number of bits (8 or 16) and returns the memory contents. The address must be a fully qualified
memory address. The RAM range is 0x7e0000 through 0x7fffff, but you may use any memory address, including
the ROM data itself.
int memory.readbytesigned(int address)
int memory.readwordsigned(int address)
v0.04+ only
Same as its counterparts, except numbers will be treated as signed. Numbers larger than 127 for bytes and
32767 for words will be translated into the correct negative numbers. For reference, an alternate formula
is to subtract 256 for bytes and 65536 for words from any number equal to or larger than half that number.
For example, a byte at 250 becomes 250-256 = -6.
memory.writebyte(int address, int value)
memory.writebyte(int address, int value)
Writes a number of bits (8 or 16) to the indicated memory address. The address MUST be in the range of
0x7e0000 through 0x7fffff.
memory.register(int address, function func)
When the given memory address is written to (range must be 0x7e0000 to 0x7fffff), the given function will be
called. The execution of the CPU will be paused mid-frame to call the given function.
Only one function can be registered with a memory address. 16 bit writes will only trigger the lower address
listener. There is no distinction between 8 and 16 bit writes. func may be nil in order to
delete a function from listening.
Code called may not call snes9x.frameadvance() or any savestate save/load functions, and any button
manipulation results are undefined. Those actions are only meaningful at frame boundaries.
Access to the gamepads. Note that Lua makes some joysticks do strange things.
Setting joypad inputs causes the user input for that frame to be ignored, but
only for that one frame.
Joypads are numbered 1 to 5.
Joypad buttons are selected by use of a table with special keys. The table
has keys start, select, up, down, left, right, A, B, X, Y, L, R. Note the
case is sensetive. Buttons that are pressed are set to a non-nil value
(use of the integer 1 is just a convention). Note that "false" is valid,
but discouraged as testing for logical true will fail.
Currently reading input from a movie file is not possible, but
a movie will record button presses from Lua.
table joypad.read(int which)
Returns a table indicating which buttons are pressed by the user.
This is probably the only way to get input to the script by the user.
This is always user input, even if the joypads have been set by joypad.set.
joypad.set(int which, table buttons)
Sets the buttons to be pressed. These choices will be made in place of
what the user is pressing during the next frame advance; they are then
discarded, so this must be called once every frame, even if you just want to
keep the same buttons pressed for several frames.
Control over the savestate process. Savestate objects are opaque structures
that represent non-player accessible states (except for the functions that
return "official" savesates). Such an object is garbage collectable, in which
case the savestate is no longer usable. Recycling of existing