Post subject: lsnes Memory Watch & Lua
Player (84)
Joined: 7/25/2011
Posts: 58
Could someone please help me with memory watch expressions in lsnes? I have a listing of addresses I want to load from a snes9x memory watch, but I don't know what syntax to use. For example, I want to see address 7E33DE and I want it to be called "HP". Also, I had a Lua script that would display various memory addresses on screen for snes9x, but now I load it in lsnes and nothing happens? I know Lua scripts that affect memory addresses work, so I'm not sure what to do. Here's a shortened version of the script: while true do emu.frameadvance() hp=memory.readbyte(0x7E33DE) gui.text(232,186,"HP: "..hp) end
Post subject: Re: lsnes Memory Watch & Lua
Emulator Coder, Skilled player (1141)
Joined: 5/1/2010
Posts: 1217
TheHepper wrote:
Could someone please help me with memory watch expressions in lsnes? I have a listing of addresses I want to load from a snes9x memory watch, but I don't know what syntax to use. For example, I want to see address 7E33DE and I want it to be called "HP".
C0x7E33DEzB C...z: Push constant B: Pop address, push contents of unsigned byte. b: Pop address, push contents of signed byte. W: Pop address, push contents of unsigned word. w: Pop address, push contents of signed word.
TheHepper wrote:
Also, I had a Lua script that would display various memory addresses on screen for snes9x, but now I load it in lsnes and nothing happens?
Something like this should work:
on_paint = function()
gui.right_gap(160);
hp = memory.readbyte(0x7E33DE)
gui.text(512, 0, "HP: "..hp)
end
(I placed the text outside the game area...)
Post subject: Re: lsnes Memory Watch & Lua
Masterjun
He/Him
Site Developer, Skilled player (1970)
Joined: 10/12/2010
Posts: 1179
Location: Germany
^ this, and you maybe wanna include "gui.subframe_update(false)" at the start of the script...
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Player (84)
Joined: 7/25/2011
Posts: 58
Thanks! So just to clarify, all addresses should start with "C0x" and end with "z", then the last letter designates how it is displayed. What if I want to put a description in, such as designating that address as "HP", is that possible? The Lua script works great, thanks for putting it outside of the screen also!
Emulator Coder, Skilled player (1141)
Joined: 5/1/2010
Posts: 1217
TheHepper wrote:
Thanks! So just to clarify, all addresses should start with "C0x" and end with "z", then the last letter designates how it is displayed. What if I want to put a description in, such as designating that address as "HP", is that possible?
The 0x after 'C' means hexadecimal constant. But if it is an address, hexadecimal is often the easiest. When you add a memory watch, it prompts for a name. You can name it as 'HP', then it gets shown as 'M[HP] <value>'. And actually, memory watch expressions can be much more complicated, like: C0x007e0876zwC0x007e002azw- Which is the difference between signed word values at 0x7e002a and C0x7e0876.
Player (84)
Joined: 7/25/2011
Posts: 58
That makes sense, thanks again! Another question: I loaded a few memory addresses and tried to save them as a .wch file. This is a shortened version of how the file looks: Screen X pix C0x7E0CE0zB Screen y pix C0x7E0CE2zB What happens is the first address shows up with a "square" character that must be deleted to display correctly. The last line shows up fine since there are no lines below it. It's not a big deal, but is there a way to prevent having to edit each address when I reload?
Emulator Coder, Skilled player (1141)
Joined: 5/1/2010
Posts: 1217
TheHepper wrote:
What happens is the first address shows up with a "square" character that must be deleted to display correctly. The last line shows up fine since there are no lines below it.
Sounds like the watch file loading CRLF bug I fixed in Δ10.
Player (84)
Joined: 7/25/2011
Posts: 58
Oh haha, I was using rr1-Δ8ε1, didn't see the new version. Glad the arrow key problem was fixed too. Thanks!
Player (136)
Joined: 9/18/2007
Posts: 389
Well, it's not memory watch, but also a Lua script from Snes9x. Just some sample code:
	for i = 1, 60 do
		joypad.set(1,{right=true,down=true,A=true})
		snes9x.frameadvance()
	end
	for i = 1, 200 do
		joypad.set(1,{})
		snes9x.frameadvance()
	end
	for i = 1, 70 do
		joypad.set(1,{L=true})
		snes9x.frameadvance()
	end
There are some limitations in lsnes making the same code impossible: There is no frame-advance Lua function in lsnes, and setting input is only allowed in the on_input callback. So here is my workaround:
button_by_index = {
	[4] = "u", [5] = "d", [6] = "l", [7] = "r",
	[8] = "A", [0] = "B", [9] = "X", [1] = "Y",
	[3] = "S", [2] = "s",[10] = "L",[11] = "R"
}

button_by_name = {}
for index,name in pairs(button_by_index) do
	button_by_name[name] = index
end

function press(keys, player)
	if player == nil or player == 1 then padnum = 0 end
	if player == 2 then padnum = 4 end
	assert(padnum ~= nil, "invalid player")

	local a = {[0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	
	for i=1,string.len(keys) do
		local index = button_by_name[string.sub(keys,i,i)]
		if index ~= nil then
			a[index] = 1
		end
	end
	
	input.seta(padnum, 0, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
end

getnextinput = coroutine.create(function()
	for i = 1, 60 do
		coroutine.yield("rdA")
	end
	
	for i = 1, 200 do
		coroutine.yield("")
	end
	
	for i = 1, 70 do
		coroutine.yield("L")
	end
end)

function on_input()
	local b, keys = coroutine.resume(getnextinput)
	if b then
		press(keys)
	end
end
Some ideas to make it less complicated?
Emulator Coder, Skilled player (1141)
Joined: 5/1/2010
Posts: 1217
partyboy1a wrote:
Some ideas to make it less complicated?
In the latest version, something like this could work:
on_input = loopwrapper(function(wait)
	for i = 1, 60 do
		input.joyset(1,{right=true,down=true,A=true})
		wait()
	end
	for i = 1, 200 do
		input.joyset(1,{})
		wait()
	end
	for i = 1, 70 do
		input.joyset(1,{L=true})
		wait()
	end
end);
(The functions loopwrapper and input.joyset are new in the latest version).
Player (136)
Joined: 9/18/2007
Posts: 389
Yay, that's a lot more convenient. This way, it will be a lot easier to translate Snes9x scripts to lsnes. There should also be input.joyget(player) returning such a table. (Then it also doesn't require something like
input.joyset(1, {right = "toggle", other = "keep"})
you can use
joykeys = input.joyget(1)
joykeys.right = not joykeys.right
input.joyset(1,joykeys)
instead.)
Emulator Coder, Skilled player (1141)
Joined: 5/1/2010
Posts: 1217
partyboy1a wrote:
There should also be input.joyget(player) returning such a table.
Well, that's implemented now. I saw the code you wrote. Looked like some simplifications could be possible by using new Lua functions on the newest version. Also, looked like implementing some new lua functions / callbacks would be in order... Ideas? Also, the controller number table is: 1st controller is physical 0 (0,0) 2nd controller is physical 4 (1,0) 3rd controller is physical 5 (1,1) 4th controller is physical 6 (1,2) 5th controller is physical 7 (1,3) 6th controller is physical 1 (1,1) 7th controller is physical 2 (1,2) 8th controller is physical 3 (1,3)
Player (136)
Joined: 9/18/2007
Posts: 389
One of the crazier ideas I got while making the inputreplayer script allow "movie playback" and "movie record" on a per-player base. So if you create a multiplayer run, and have recorded enough for player one, you could load some state and start recording for player two while preserving input from player one... Maybe something like this can already be done with TAS movie editor, but I'm not sure. In general, I think that someone using the emulator shouldn't need any knowledge about the internal structure of the SNES. It should be like a blackbox which can be used in the most understandable way. And with the tables in inputreplayer, the SNES comes closer to this black box. And when there is an instruction like input.set(5,5,1), this requires details from inside the black box. input.joyset(player, buttontable) doesn't require the same knowledge, which is actually a good thing. Where more knowledge is needed, there exist more sources for errors. An even more extreme example would be extracting graphics There could be a command like graphics.get("sprites") which returns a table {[1] = {x = 12, y = 32, verticalflip = true, horizontalflip = false, bitmap = somebitmap}, [2] = ... }. (Well, I don't know if anything like that is possible with the bsnes core.) You wouldn't even have to know that there is some OAM which has some of these information, and it needs absolutely no knowledge about the bitplane format used by SNES games.