I know that there are video overlays that one can use with OBS to record the state of a controller at any given moment, what I want is to re-create such a thing within MAME itself in order to compare the two.
All I really want is for there to be well-positioned boxes for: D-pad L/R/U/D, and Buttons 1-4. If it's possible to make a Lua script to read the current inputs that MAME is registering, and either fill or not-fill a surrounding with a highlight whenever that button is considered to be pressed, that would be a huge help, because if it's overlaid on the window, then I'll capture it while recording a given session. I can then see where the disagreement originates: If MAME's idea of "which button is pressed" differs from the controller overlay, then that could point the finger one way. If not, then it makes it clearer that the MAME driver for the NES itself is flawed in how it handles inputs.
If you just want to display inputs as seen by the emulated system, you’re better off just making a layout file. Use the inputs to set the state of elements, and make them draw a different colour box depending on the state. You can do it with Lua script, but it’s a lot more work. If you ask over at the MAME Artwork forum, most of the more experienced guys there have mastered doing this for simple cases, so you could probably get more detailed examples from them. Also it’s kind of hap’s thing.
Ok, here's a little script that will show the buttons for the left controller:
Code
function iifnot0(a,b,c) if a~=0 then return b else return c end end
function drawoverlay()
dpadx,dpady = 50, 170 padx,pady = 30, 30 cposx,cposy = 120, 170 rposx, rposy = 190,170
buttons = {up = 16, down = 32, left = 64, right = 128, a=1, b=2, select=4, start=8}
buttonpos = {up = {dpadx, dpady-pady}, down = {dpadx,dpady + pady}, right = {dpadx+padx, dpady}, left={dpadx-padx, dpady}, select={cposx, cposy}, start={cposx+padx,cposy}, b={rposx,rposy},a={rposx+padx,rposy}}
for i,j in pairs(buttons) do
manager.machine.screens[":screen"]:draw_box(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20,
0x88ffffff, iifnot0(manager.machine.ioport.ports[":ctrl1:joypad:JOYPAD"]:read() & buttons[i], 0x88ff0000, 0x88000000))
manager.machine.screens[":screen"]:draw_text(buttonpos[i][1]+2,buttonpos[i][2]+4,i)
end
end
emu.register_frame_done(drawoverlay)
You can paste it in on the lua console or save it to a file and dofile("myfile.lua")
For fun, I thought I'd add the ability to move the overlay up and down with keypad 2 and 8, adjust the alpha with keypad 4 and 6, and turn the overlay off and on with [ and ]
Code
function iifnot0(a,b,c) if a~=0 then return b else return c end end
displayoverlay=true
originx,originy=50,170
alphavalue=0.5
function drawoverlay()
dpadx,dpady = 50,originy padx,pady = 30,30 cposx,cposy = 120,originy rposx, rposy = 190,originy
buttons = {up = 16, down = 32, left = 64, right = 128, a=1, b=2, select=4, start=8}
buttonpos = {up = {dpadx, dpady-pady}, down = {dpadx,dpady + pady}, right = {dpadx+padx, dpady}, left={dpadx-padx, dpady}, select={cposx, cposy}, start={cposx+padx,cposy}, b={rposx,rposy},a={rposx+padx,rposy}}
if displayoverlay==true then
for i,j in pairs(buttons) do
manager.machine.screens[":screen"]:draw_box(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20,
0x00ffffff | math.floor(alphavalue*255)<<24,
iifnot0(manager.machine.ioport.ports[":ctrl1:joypad:JOYPAD"]:read() & buttons[i],
0x00ff0000 | math.floor(alphavalue*255)<<24,
0x00000000 | math.floor(alphavalue*255)<<24))
manager.machine.screens[":screen"]:draw_text(buttonpos[i][1]+2, buttonpos[i][2]+4, i,
0xffffff | math.floor(alphavalue*255)<<24)
end
end
input=manager.machine.input
if input:code_pressed(input:code_from_token("KEYCODE_OPENBRACE")) then displayoverlay = false end
if input:code_pressed(input:code_from_token("KEYCODE_CLOSEBRACE")) then displayoverlay = true end
if input:code_pressed(input:code_from_token("KEYCODE_8PAD")) then originy = originy - 1 end
if input:code_pressed(input:code_from_token("KEYCODE_2PAD")) then originy = originy + 1 end
if input:code_pressed(input:code_from_token("KEYCODE_4PAD")) then alphavalue = alphavalue-0.01 if alphavalue<0 then alphavalue=0 end end
if input:code_pressed(input:code_from_token("KEYCODE_6PAD")) then alphavalue = alphavalue+0.01 if alphavalue>1.0 then alphavalue=1.0 end end
end
emu.register_frame_done(drawoverlay)
By the way, I do apologize for not having had a chance to dig into this. My weekend was topsy-turvy schedule-wise, and this week I just don't have much spare time. I'll get onto this this coming weekend.
If it helps, I added a framecounter display on the layout.
Interestingly, it seems to use up all the textures if you set the maxstate of the counter to something like 999999. If I omit the maxstate, it will "wrap" the counter at 2048.
FRAME:2045 Searching font Liberation Sans in -. path/s Matching font: 0x55578a018070 FRAME:2046 Searching font Liberation Sans in -. path/s Matching font: 0x55578a957cf0 FRAME:2047 Searching font Liberation Sans in -. path/s Matching font: 0x55578a983500 FRAME:2048 FRAME:2049 FRAME:2050
and if I set the maxstate to "99999" then it will count up to 4523 and fatal error FRAME:4522 Searching font Liberation Sans in -. path/s Matching font: 0x55c577436580 FRAME:4523 Searching font Liberation Sans in -. path/s Matching font: 0x55c5774f96f0 Ignoring MAME exception: renderer_ogl::texture_create: texture hash exhausted ... Fatal error: renderer_ogl::texture_create: texture hash exhausted ... Average speed: 99.42% (74 seconds)
function iifnot0(a,b,c) if a~=0 then return b else return c end end
displayoverlay=true
originx,originy=50,170
alphavalue=0.5
function drawoverlay()
dpadx,dpady = 50,originy padx,pady = 30,30 cposx,cposy = 120,originy rposx, rposy = 190,originy
buttons = {up = 16, down = 32, left = 64, right = 128, a=1, b=2, select=4, start=8}
buttonpos = {up = {dpadx, dpady-pady}, down = {dpadx,dpady + pady}, right = {dpadx+padx, dpady}, left={dpadx-padx, dpady}, select={cposx, cposy}, start={cposx+padx,cposy}, b={rposx,rposy},a={rposx+padx,rposy}}
if displayoverlay==true then
for i,j in pairs(buttons) do
manager.machine.screens[":screen"]:draw_box(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20,
0x00ffffff | math.floor(alphavalue*255)<<24,
iifnot0(manager.machine.ioport.ports[":ctrl1:joypad:JOYPAD"]:read() & buttons[i],
0x00ff0000 | math.floor(alphavalue*255)<<24,
0x00000000 | math.floor(alphavalue*255)<<24))
manager.machine.screens[":screen"]:draw_text(buttonpos[i][1]+2, buttonpos[i][2]+4, i,
0xffffff | math.floor(alphavalue*255)<<24)
end
framenum = manager.machine.screens[":screen"]:frame_number()
manager.machine.screens[":screen"]:draw_text(5,5,"Frame Number: "..framenum,
0xffffff | math.floor(alphavalue*255)<<24)
manager.machine.screens[":screen"]:draw_text(5,5,"Frame Number: "..framenum.." "..string.rep(" ",framenum % 10 * 2)..framenum % 10,
0xffffff | math.floor(alphavalue*255)<<24)
end
input=manager.machine.input
if input:code_pressed(input:code_from_token("KEYCODE_OPENBRACE")) then displayoverlay = false end
if input:code_pressed(input:code_from_token("KEYCODE_CLOSEBRACE")) then displayoverlay = true end
if input:code_pressed(input:code_from_token("KEYCODE_8PAD")) then originy = originy - 1 end
if input:code_pressed(input:code_from_token("KEYCODE_2PAD")) then originy = originy + 1 end
if input:code_pressed(input:code_from_token("KEYCODE_4PAD")) then alphavalue = alphavalue-0.01 if alphavalue<0 then alphavalue=0 end end
if input:code_pressed(input:code_from_token("KEYCODE_6PAD")) then alphavalue = alphavalue+0.01 if alphavalue>1.0 then alphavalue=1.0 end end
end
emu.register_frame_done(drawoverlay)
printt(manager.machine.ioport.ports[":ctrl1:mdpad:PAD"].fields) P1 B sol.ioport_field*: 0x564f7ec3fff8 %p B sol.ioport_field*: 0x564f7ec46198 %p A sol.ioport_field*: 0x564f7ec56848 P1 Right sol.ioport_field*: 0x564f7ec2b2b8 P1 Up sol.ioport_field*: 0x564f7ecfc988 P1 A sol.ioport_field*: 0x564f7ec202c8 P1 Start sol.ioport_field*: 0x564f7ec41338 %p C sol.ioport_field*: 0x564f7ec567f8 P1 C sol.ioport_field*: 0x564f7ec5a1d8 P1 Down sol.ioport_field*: 0x564f7f703e88 P1 Left sol.ioport_field*: 0x564f7ec60e08
and then look at the mask:
for i,j in pairs(manager.machine.ioport.ports[":ctrl1:mdpad:PAD"].fields) do print(i, j.mask) end P1 Down 2 P1 C 32 P1 Left 4 P1 Up 1 P1 B 16 %p A 64 %p C 32 %p B 16 P1 Start 128 P1 Right 8 P1 A 64
EDIT: Corrected 1 problem, got another Turns out it was a misplaced /n No more errors but I can't see the text on the buttons, still.
/edit
I'm probably doing something stupid as it is my 1st time looking at LUA but I have trouble making this work for the genesis. I can see the layout but the boxes are not filled with the text. They change to red in the correct positions when I press the buttons too, so I'm assuming the pad is detected correctly.
The error is in line 13 on this .lua script
The error is: "attempt to index a nil value (field ':screen')" and line 13 would be "manager.machine.screens[":screen"]:draw_box(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20," For the sake of debug I removed the "manager.machine.screens[":screen"]" and then the error moves to draw_box
Quote
function iifnot0(a,b,c) if a~=0 then return b else return c end end
function drawoverlay() dpadx,dpady = 50,originy padx,pady = 30,30 cposx,cposy = 120,originy rposx,rposy = 190,originy buttons = {up = 1, down = 2, left = 4, right = 8, b = 16, c = 32, a = 64, start = 128} buttonpos = {up = {dpadx, dpady-pady}, down = {dpadx,dpady+pady}, right = {dpadx+padx, dpady}, left={dpadx-padx, dpady}, c={cposx, cposy}, start={cposx+padx, cposy}, b={rposx,rposy}, a={rposx+padx, rposy}} if displayoverlay==true then for i,j in pairs(buttons) do manager.machine.screens[":screen"]:draw_box(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20, 0x00ffffff | math.floor(alphavalue*255)<<24, iifnot0(manager.machine.ioport.ports[":ctrl1:mdpad:PAD"]:read() & buttons[i], 0x00ff0000 | math.floor(alphavalue*255)<<24, 0x00000000 | math.floor(alphavalue*255)<<24)) manager.machine.screens[":screen"]:draw_text(buttonpos[i][1]+2, buttonpos[i][2]+4, i, 0xffffff | math.floor(alphavalue*255)<<24) end end input=manager.machine.input if input:code_pressed(input:code_from_token("KEYCODE_OPENBRACE")) then displayoverlay = false end if input:code_pressed(input:code_from_token("KEYCODE_CLOSEBRACE")) then displayoverlay = true end if input:code_pressed(input:code_from_token("KEYCODE_8PAD")) then originy = originy - 1 end if input:code_pressed(input:code_from_token("KEYCODE_2PAD")) then originy = originy + 1 end if input:code_pressed(input:code_from_token("KEYCODE_4PAD")) then alphavalue = alphavalue-0.01 if alphavalue<0 then alphavalue=0 end end if input:code_pressed(input:code_from_token("KEYCODE_6PAD")) then alphavalue = alphavalue+0.01 if alphavalue>1.0 then alphavalue=1.0 end end end
emu.register_frame_done(drawoverlay)
So, if this is something quick and you have a spare 5m I'll like your help on this. Thanks in advance.
Each driver is free to name its screens differently, so in this case the screen is named ":megadriv"
Run a search and replace from ":screen" to ":megadriv" and it should work
function printt(t) for i,j in pairs(t) do print (i,j) end end printt(manager.machine.screens) :megadriv sol.screen_device*: 0x557f97832d78
Code
function iifnot0(a,b,c) if a~=0 then return b else return c end end
displayoverlay=true
originx,originy=50,170
alphavalue=0.5
function calcalpha() return math.floor(alphavalue*255)<<24 end
function pressed(token) input=manager.machine.input return input:code_pressed(input:code_from_token(token)) end
function drawoverlay()
dpadx,dpady = 50,originy padx,pady = 30,30 cposx,cposy = 120,originy rposx,rposy = 190,originy
buttons = {up = 1, down = 2, left = 4, right = 8, b = 16, c = 32, a = 64, start = 128}
buttonpos = {up = {dpadx, dpady-pady}, down = {dpadx,dpady+pady}, right = {dpadx+padx, dpady}, left={dpadx-padx, dpady}, c={cposx, cposy}, start={cposx+padx, cposy}, b={rposx,rposy}, a={rposx+padx, rposy}}
if displayoverlay==true then
screenname = ":screen"
screenname = ":megadriv"
screen = manager.machine.screens[screenname]
for i,j in pairs(buttons) do
screen:draw_box(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20,
0x00ffffff | calcalpha(),
iifnot0(manager.machine.ioport.ports[":ctrl1:mdpad:PAD"]:read() & buttons[i],
0x00ff0000 | calcalpha(),
0x00000000 | calcalpha()))
screen:draw_text(buttonpos[i][1]+2, buttonpos[i][2]+4, i, 0xffffff | calcalpha())
end
end
if pressed("KEYCODE_OPENBRACE") then displayoverlay = false end
if pressed("KEYCODE_CLOSEBRACE") then displayoverlay = true end
if pressed("KEYCODE_8PAD") then originy = originy - 1 end
if pressed("KEYCODE_2PAD") then originy = originy + 1 end
if pressed("KEYCODE_4PAD") then alphavalue = alphavalue-0.01 if alphavalue<0 then alphavalue=0 end end
if pressed("KEYCODE_6PAD") then alphavalue = alphavalue+0.01 if alphavalue>1.0 then alphavalue=1.0 end end
end
emu.register_frame_done(drawoverlay)
For fun, I wanted to see if I could get lua to generate some xml. So this makes a list of elements from drawing boxes and text, then prints some xml.
Code
elementlist = {}
function iifnot0(a,b,c) if a~=0 then return b else return c end end
function mydrawbox(x,y,x1,y1,portname,mask,bordercolor,inside1,inside0)
manager.machine.screens[":screen"]:draw_box(x,y,x1,y1,bordercolor, iifnot0(manager.machine.ioport.ports[portname]:read() & mask, inside1, inside0))
table.insert(elementlist, {type="box",x=x,y=y,x1=x1,y1=y1,portname=portname,mask=mask,bordercolor=bordercolor,inside1=inside1,inside0=inside0})
end
function mydrawtext(x,y,text,col)
manager.machine.screens[":screen"]:draw_text(x,y,text,col)
table.insert(elementlist, {type="text",x=x,y=y,text=text,col=col})
end
function otag(t,attrs,inside) attrs=attrs or "" return "<"..t.." "..attrs..">\n" end -- open tag
function ctag(t) return "</"..t..">\n" end -- close tag
function tag(t,attrs,inside) attrs=attrs or "" inside = inside or "" return "<"..t.." "..attrs..">\n"..inside.."</"..t..">\n" end -- complete tag
function stag(t,attrs) attrs=attrs or "" return "<"..t.." "..attrs.."/>\n"end -- singleton tag
function attr(a,v) return a.."=\""..v.."\"".." " end -- attribute
function makelayout()
print(otag("mamelayout",attr("version",2)))
print(tag("element",attr("name","button"),
tag("rect","",stag("color",attr("red",1.0)..attr("green",1.0)..attr("blue",1.0)))..
tag("rect",attr("state",1),stag("color",attr("red",1.0)..attr("green",0.0)..attr("blue",0.0))..
stag("bounds",attr("x",0.05)..attr("y",0.05)..attr("width",0.90)..attr("height",0.90)))..
tag("rect",attr("state",0),stag("color",attr("red",0.0)..attr("green",0.0)..attr("blue",0.0))..
stag("bounds",attr("x",0.05)..attr("y",0.05)..attr("width",0.90)..attr("height",0.90))) ))
for i,j in pairs(elementlist) do
if j.type=="text" then print(tag("element",attr("name","text"..j.text),stag("text",attr("string",j.text)))) end
end
print(otag("view",attr("name","Overlay")))
print([[<screen index="0">
<bounds x="0" y="0" width="256" height="240" />
</screen>]])
for i,j in pairs(elementlist) do
if j.type=="text" then print(tag("element",attr("ref","text"..j.text),stag("bounds",attr("top",j.y)..attr("left",j.x)..attr("bottom",j.y+10)..attr("right",j.x+4*j.text:len())))) end
if j.type=="box" then print(tag("element",attr("ref","button")..attr("inputtag",j.portname)..attr("inputmask",j.mask),stag("bounds",attr("top",j.y)..attr("left",j.x)..attr("bottom",j.y1)..attr("right",j.x1)))) end
end
print(ctag("view"))
print(ctag("mamelayout"))
end
function drawoverlay()
elementlist = {}
dpadx,dpady = 50, 170 padx,pady = 30, 30 cposx,cposy = 120, 170 rposx, rposy = 190,170
buttons = {up = 16, down = 32, left = 64, right = 128, a=1, b=2, select=4, start=8}
buttonpos = {up = {dpadx, dpady-pady}, down = {dpadx,dpady + pady}, right = {dpadx+padx, dpady}, left={dpadx-padx, dpady}, select={cposx, cposy}, start={cposx+padx,cposy}, b={rposx,rposy},a={rposx+padx,rposy}}
for i,j in pairs(buttons) do
mydrawbox(buttonpos[i][1],buttonpos[i][2],buttonpos[i][1]+20,buttonpos[i][2]+20,
":ctrl1:joypad:JOYPAD",buttons[i], 0x88ffffff, 0x88ff0000, 0x88000000)
mydrawtext(buttonpos[i][1]+2,buttonpos[i][2]+4,i,0x88ffffff)
end
end
emu.register_frame_done(drawoverlay)
makelayout()
and a lua script (executed with dofile) that simply watches the dummy port and executes a command based on the port being pressed.
Code
function press(port,field)
manager.machine.ioport.ports[port].fields[field]:set_value(1)
end
function release(port,field)
manager.machine.ioport.ports[port].fields[field]:clear_value()
end
function do_reset()
press(":keyb_special","Control")
press(":keyb_special","RESET")
emu.wait(2/60)
release(":keyb_special","RESET")
release(":keyb_special","Control")
emu.wait(1/60)
end
function do_ctrlc()
press(":keyb_special","Control")
press(":X3","C c") -- was aborting because it was looking for "c"
emu.wait(2/60)
release(":X3","C c")
emu.wait(1/60)
release(":keyb_special","Control")
emu.wait(1/60)
press(":X6","Return") -- not in X5 it's in X6!!!
emu.wait(1/60)
release(":X6","Return")
emu.wait(1/60)
end
function check_dummy()
if lastdummyread == nil then lastdummyread=0 end
dummyread = manager.machine.ioport.ports[":keyb_dummy"]:read()
if dummyread ~= 0 and lastdummyread==0 then
if dummyread & 4 ~= 0 then
co1=coroutine.create(do_reset) coroutine.resume(co1)
end
if dummyread & 2 ~= 0 then
co1=coroutine.create(do_ctrlc) coroutine.resume(co1)
end
end
lastdummyread = dummyread
end
emu.register_frame_done(check_dummy)
I never knew about the function keys on the Franklin Ace 500/2x00. That's really cool. I wish that I'd had one of those back in the day just for that feature.
Perusing the code, I made a few modifications and the function keys seem to work, (at least on initial testing, not extensively tested though).
I think the problem is that it hits the strobe when you press the key AND when you release the key and there's no key down so it returns 0 and basically types the @ symbol.