Post subject: How emulators handle joypad and inputs from lua.
Editor, Skilled player (1202)
Joined: 9/27/2008
Posts: 1085
I have played around with only four emulators: - FCEUX - Snes9x - VBA - DeSmuME I can only tell you what I know of these ones, as I haven't tried out the others. However, there are subtle differences that will come up if you attempt to handle the joypads in another emulator. The exact same thing you've been doing in one emulator may completely fail in another. First, I will refer to one of four registers, "Boundary", "Before", "After", and "Gui". A joypad.get in one register may not necessarily give the same result as joypad.get in another register. Some details of what exactly I mean about them: "Boundary" - A piece of code you typically place in a loop with emu.frameadvance(). "Before" - Created a function that's passed to emu.registerbefore as a parameter. Typically, this is after the framecount and joypads are updated, but before the actual frame of emulation takes place. Apparently, not always true among all emulators. "After" - A function passed to emu.registerafter. Usually, this would be after the frame is emulated. Apparently, this isn't always true between emulators. "Gui" - Function passed to gui.register. This function would be called when the emulator updates the display. For some, this happens only once per frame. For others, they repeatedly call this function while paused, which is a mighty useful effect when dealing with user input. In addition, the following functions are the focus of this post: joypad.get - Usually takes a number representing the player whose input you wish to read. It simply reads the joypad, whatever state it may be in at the time. joypad.peek - Usually takes a number, like with joypad.get. However, it reads whatever keys the user wants to press, rather than the previous frame's input. The difference is subtle, but very clear if it's possible to run the lua code while the emulation is paused -- get will always return the same table, but peek will keep changing with the user's key presses while the emulation is paused. joypad.set - Usually takes a number representing the player, and always takes a table representing the input you want done next frame. Depending on the register you place this in, it may take place immediately, wait until next frame, or is completely ignored. input.get - No parameters. Returns the state of the keyboard and mouse in a table. When dealing with joypad input, you usually don't want to use this, unless you're using a piece of "run-while-paused" code without joypad.peek available. However, this does open another avenue of working with user input, and is useful for creating runtime options in more complex scripts. I have gone into detail of each component I will talk about as I analyze each emulator I have tried. I wish for you to get a brief understanding as I do about each component so that you can read that which follows with an intuition that better approximates my own. I would prefer that you don't get lost while reading my stuff. But now's the time I go into each emulator I have tried... FCEUX 2.1.4 joypad.get returns the previous frame's input on "Boundary", and the current frame's input on "Before", "After", and "Gui". Reads movie input. There is no joypad.peek in this emulator, as of time of this writing. joypad.set will apply the input immediately on "Boundary", and will apply it next frame in the other registers. "Gui" has the advantage of running while emulation is paused, but I recommend having it affect another variable that "Boundary" can then read and set the joypads as appropriate. joypad.set has four options for each button, giving complete control: - true (force the button to be pressed) - false (force the button unpressed) - nil (leave control of the button to the user) - "invert" (reverses the user's control of the button) Critical error: With how get and set works, it is impossible to read the joypad on the current frame, decide what it means, then change the joypad before emulation needs it. On "Boundary", you pick up the previous frame's input, not the current frame. On "Before", you get the current frame's input, but joypad.set() can't do a thing to the joypad now. This feels like a design issue to me. However, the fact joypad.set does allow for four options and the fact "Gui" is run while emulation is paused gives a great deal of control. Should joypad.peek be implemented, I hope to see it gain perfect control. Snes9x 1.51 v6 joypad.get gets the current frame's input in any register, even "Boundary". It will read changes made by lua, but lua won't get the chance to do so at "Boundary". Reads movie input. There is no joypad.peek in this emulator, as of time of this writing. joypad.set will apply the input immediately in "Boundary" and is completely ignored in any other register. This has caused great confusion in me when I was transitioning from FCEUX to Snes9x lua, but I since learned emulators act differently. It is not possible to have code run while emulation is paused, except with a fake pause by repeatedly loading the same savestate every frame. joypad.set has only two options, however: - nil (forces the button unpressed) - anything not nil (forces the button to be pressed) A critical error in joypad manipulation, such as one seen in FCEUX and VBA, is averted, due to the fact you get the immediate input and can change it before the emulation updates. I am quite thankful for this, as there's no way to run code while emulation is paused. There is a lua-side workaround to the limited joypad options: Download JoypadSetSnes9x.lua
Language: lua

function JoypadSetSnes9x(player,inputs) local TempInput= joypad.get(player) for btn,val in pairs(inputs) do -- This inherently skips nil if not val then -- it's obviously set to false by user. TempInput[btn]= false elseif type(val) == "string" then -- Mimic FCEUX's "invert" option TempInput[btn]= not TempInput[btn] else -- It effectively evaluates to true otherwise. TempInput[btn]= true end end joypad.set(player, TempInput) end
The joypad control is done quite well here. Although it lacks the four options like FCEUX, a lua-side workaround is possible thanks to the fact you can read and manipulate the joypad before the emulation gets a hold of it. I just only wish that lua can freely run while emulation is paused so that we can get perfect control. VBA v22 joypad.get gets the previous frame's input on "After" and the current frame's input on "Boundary", "Before", and "Gui". Movie data is not read. There is no joypad.peek in this emulator, as of time of this writing. joypad.set will apply the input immediately on "After", and waits until the next frame on "Bondary", "Before", and "Gui". I fought hard trying to figure out what logic the darn emulator was using. I finally got it: "After" takes place before "Boundary", "Before", and "Gui", so any variables set in the latter three registers isn't seen by "After" until the next frame. Like with Snes9x, there's no way to run lua code while emulation is paused, except hacking it by repeatedly loading the same state every frame. joypad.set has exactly two options, just like in Snes9x: - nil (forces the button unpressed) - anything not nil (forces the button to be pressed) Critical error: It is impossible to read the current joypad to make decisions of the current situation. There are no joypad-related workarounds from lua-side. If you absolutely need to hijack joypad control from lua while still reading the user's input intelligently, do it in "After" and use input.get instead. This completely negates the user's setup from the emulator side, but it's at least something. Furthermore, with how the registers work, you can't mimic FCEUX's joypad.set options from lua. There's also no way to run lua code while emulation is paused. Then there's the fact it doesn't read movie input. The whole thing combines into a horrible mess that I have a very difficult time working with. Someone. Hear my plea. Get joypad.get to read movie data. Fix the registers so that I can get and immediately set before the emulation updates. Allow code to run while paused. I don't know how, but if you developers can do it, you will have my thanks. As it stands, VBA is torturous to use in my attempts to produce a viable multitrack script. DeSmuME 0.9.6 joypad.get reads the previous frame's input in "Boundary" and "Before". I haven't checked if it reads movie data. There's also a separate stylus.get function which works similarly, except for use with the touchscreen controls. joypad.peek works well in this emulator. It picks up whatever buttons the user is holding at the time. As well, there is stylus.peek like above. joypad.set immediately applies input on "Boundary" and "Before". Similarly, stylus.set works as such, too. Code can run while paused under "Gui". Excellent... joypad.set has three options for each button. Almost there, but not quite: - true (force the button to be pressed) - false (force the button unpressed) - nil (leave control of the button to the user) No critical errors here. Although joypad.get and joypad.set are incompatible together like with VBA and FCEUX, joypad.peek gets around that problem by looking at the user's input rather than the previous frame's input. If you want the "invert" option that exists for FCEUX, use the Snes9x code I've written up there, except replace joypad.get with joypad.peek and get rid of all instances of the identifier player (and accompanying comma, if any). Of the four emulators I've tried, this is the only one to provide perfect joypad control through lua. I'd tweak joypad.set to follow the four options present in FCEUX, but otherwise, I'm pretty satisfied with what's there. A great job has been done here. Seeing as I haven't tried other emulators, this is where I stop. Let me know of anything I missed. I want to at least ensure someone is aware that the joypad manipulation for one emulator will generally break horribly in another. Your Snes9x joypad.get & joypad.set manipulation will fail in FCEUX and VBA, and you need to be aware of joypad.peek to get it to work right in DeSmuME. Remember, these are simply my views on how each emulator works. I'm not saying my word is the absolute truth, I could be wrong in a few places. I started this thread so that any problems are more visible and hopefully help others to come up with tests of their own to see the inner mechanics of the lua in each emulator.
P.JBoy
Any
Editor
Joined: 3/25/2006
Posts: 850
Location: stuck in Pandora's box HELLPP!!!
I can explain VBA's timing: The "After" code is executed immediately after the current frame has finished, before the next frame. The "Before" code (and "Boundary" too I think) is executed immediately after the input is received for the next frame, but before any execution of ROM code (including updating 0x04000130). The "Gui" code is executed only when VBA writes to the screen. This take into account frame-skipping as well, and will only be executed every 5 frames if the frame-skip is set to 4 for example
Editor, Skilled player (1202)
Joined: 9/27/2008
Posts: 1085
To the extent of my observation, the "After" register is called after the user hits the frame advance key (or let frames get played out while running), but before "Boundary" or "Before" runs. However, I've only used GB ROMs, and not GBA ROMs, so if the lua reacts any differently here, this would be news to me. I have not been able to read the joypad then immediately change the input feed to the emulation successfully. "After" has been the closest thing I've got to instant user control through frame-advance, and even then, I need to call upon input.get instead of joypad.get with script-specific controls overriding the usual emulator controls. The fact "Gui" is updated potentially less frequently due to frame skip makes sense. After all, it should be kind of tied to the display, so if it isn't updating the display, it doesn't surprise me that "Gui" isn't called on those undisplayed frames.
Post subject: A way to get emulator state pause/unpause?
Joined: 1/26/2009
Posts: 558
Location: Canada - Québec
FatRatKnight wrote:
there's no way to run lua code while emulation is paused, except hacking it by repeatedly loading the same state every frame.
I'm working on a script with the ability of redrawing the screen with a similar method(for pcsxrr), but I want to avoid as much as possible to create a savestate on every frame... In order to do this, I need a function that could tell me what is the speed state of the emulator, so paused or unpaused should be enough. Do you know a way to do this? If there no way, I think that could manage the hotkey by myself for frame advance or pause to keep know what are the emulator state, but this method is somewhat brutal.
Post subject: Re: How emulators handle joypad and inputs from lua.
Joined: 1/26/2009
Posts: 558
Location: Canada - Québec
FatRatKnight wrote:
"Boundary" - A piece of code you typically place in a loop with emu.frameadvance().
I think I just understood what you mean by "boundary" now, as experiment myself that we can't emu.frameadvance() across a "custom hotkey" (done with gui.register(Fn) so it can run while paused) So question: Would it be possible to have a "custom key" with the ability to call emu.frameadvance() "out of the boundary"(random idea: maybe by delaying at the right time or creating somekind of new lua instance(?), etc..) ?
Post subject: Re: A way to get emulator state pause/unpause?
Emulator Coder, Skilled player (1310)
Joined: 12/21/2004
Posts: 2687
BadPotato wrote:
If there no way, I think that could manage the hotkey by myself for frame advance or pause to keep know what are the emulator state, but this method is somewhat brutal.
I don't know how much other emulators would support this, but here's a (somewhat lame) attempt at doing this that works in Gens as it is now, showing gui.register code running while the game is paused, assuming for this example that you use P and F for pause and frame advance and that they aren't configured as your real hotkeys for anything:
pauseKey = 'P'
frameAdvanceKey = 'F'

function handlePauseAndFrameAdvance()
  local newPaused = paused
  local pauseButtonHeldNow = input.get()[pauseKey]
  if pauseButtonHeldNow ~= pauseButtonHeld then
    if pauseButtonHeldNow then newPaused = not paused end
    pauseButtonHeld = pauseButtonHeldNow
  end
  local frameAdvanceButtonHeldNow = input.get()[frameAdvanceKey]
  if frameAdvanceButtonHeldNow ~= frameAdvanceButtonHeld then
    newPaused = not frameAdvanceButtonHeldNow
    frameAdvanceButtonHeld = frameAdvanceButtonHeldNow
    frameAdvanceButtonHeldCount = 0
  elseif frameAdvanceButtonHeld then
    frameAdvanceButtonHeldCount = frameAdvanceButtonHeldCount + 1
    newPaused = frameAdvanceButtonHeldCount < 60 or (frameAdvanceButtonHeldCount % 8 ~= 0)
  end
  if paused ~= newPaused then
    paused = newPaused
    if paused then sound.clear() end
  end
end

gui.register(function()
  testcounter = (testcounter or 0) + 1
  gui.drawtext(50,50, string.format("%s -- %d", paused and "paused" or "unpaused", testcounter))
end)

while true do
  handlePauseAndFrameAdvance()
  if paused then
    gens.redraw()
    gens.wait()
  else
    gens.frameadvance()
  end
end
But really, there should be some sort of emu.registeridle() function, since this example is quite a hacky way of doing it, and updating things in the GUI drawing like some emulators let you do is just a hack as well.