Expand shared Sprinter MCP with managed launch, raw reads and key input
This commit is contained in:
@@ -8,6 +8,8 @@ function exports.startplugin()
|
||||
local lfs = require("lfs")
|
||||
local directory = assert(os.getenv("SDBG_IPC_DIR"), "Нужен SDBG_IPC_DIR")
|
||||
local session = assert(os.getenv("SDBG_SESSION_ID"), "Нужен SDBG_SESSION_ID")
|
||||
local snapdir = os.getenv("SDBG_SNAP_DIR")
|
||||
local snapshot_sequence = 0
|
||||
local generation, sequence = 0, 0
|
||||
local events, owned = {}, {}
|
||||
local state, pending = "initializing", nil
|
||||
@@ -53,6 +55,37 @@ function exports.startplugin()
|
||||
assert(type(value)=="number" and value%1==0 and value>=0 and value<=limit,"Недопустимое число")
|
||||
return value
|
||||
end
|
||||
local function first_screen()
|
||||
for _,screen in pairs(machine().screens) do return screen end
|
||||
error("Экран MAME не найден")
|
||||
end
|
||||
local function share_info()
|
||||
local result={shares={},regions={}}
|
||||
for tag,share in pairs(machine().memory.shares) do
|
||||
result.shares[#result.shares+1]={tag=tag,size=share.size}
|
||||
end
|
||||
for tag,region in pairs(machine().memory.regions) do
|
||||
result.regions[#result.regions+1]={tag=tag,size=region.size}
|
||||
end
|
||||
table.sort(result.shares,function(a,b) return a.tag<b.tag end)
|
||||
table.sort(result.regions,function(a,b) return a.tag<b.tag end)
|
||||
return result
|
||||
end
|
||||
local function read_share(tag,address,length)
|
||||
assert(type(tag)=="string" and #tag>0 and #tag<=128,"Нужен точный tag share")
|
||||
local share=machine().memory.shares[tag]
|
||||
assert(share,"Share не найден: "..tag)
|
||||
local size=number(share.size,0x10000000)
|
||||
address=number(address,size)
|
||||
length=number(length,4096)
|
||||
assert(length>=1 and address+length<=size,"Чтение за пределами share")
|
||||
local bytes={}
|
||||
for offset=0,length-1 do
|
||||
bytes[#bytes+1]=string.format("%02x",share:read_u8(address+offset))
|
||||
end
|
||||
return {tag=tag,address=address,length=length,size=size,hex=table.concat(bytes),
|
||||
generation=generation}
|
||||
end
|
||||
local invalidate
|
||||
local function dispatch(request)
|
||||
assert(request.session==session,"Чужая сессия")
|
||||
@@ -65,9 +98,13 @@ function exports.startplugin()
|
||||
session_started=true
|
||||
return {protocol=1,session=session,capabilities={snapshot=true,memory=true,
|
||||
instruction_step=true,step_over=true,step_out=true,
|
||||
bank_guard=true,deferred_breakpoints=true,console_print=true},state=state}
|
||||
bank_guard=true,deferred_breakpoints=true,console_print=true,
|
||||
shares=true,screen_pixels=true,screen_snapshot=snapdir~=nil,
|
||||
program_memory=true,list_ports=true},state=state}
|
||||
elseif command=="snapshot" then
|
||||
local result={state=state,time=now(),paused=machine().paused}
|
||||
local screen=first_screen()
|
||||
result.frame=screen:frame_number()
|
||||
result.keyboards=setmetatable({}, {__jsontype="object"})
|
||||
for tag,kbd in pairs(machine().natkeyboard.keyboards) do
|
||||
result.keyboards[tag]=kbd.enabled
|
||||
@@ -149,6 +186,75 @@ function exports.startplugin()
|
||||
bytes[#bytes+1]=string.format("%02x",symbols:memory_value(":maincpu","p",address+offset,1,true))
|
||||
end
|
||||
return {hex=table.concat(bytes)}
|
||||
elseif command=="program_memory" then
|
||||
stopped(request)
|
||||
local address=number(args.address,0x3ffff)
|
||||
local length=number(args.length,4096)
|
||||
assert(length>=1 and address+length<=0x40000,
|
||||
"Чтение за пределами raw program space")
|
||||
local symbols=emu.symbol_table(cpu())
|
||||
local bytes={}
|
||||
for offset=0,length-1 do
|
||||
bytes[#bytes+1]=string.format("%02x",symbols:memory_value(
|
||||
":maincpu","p",address+offset,1,true))
|
||||
end
|
||||
return {space="program",address=address,length=length,
|
||||
hex=table.concat(bytes),generation=generation}
|
||||
elseif command=="list_ports" then
|
||||
local result={ports={},truncated=false}
|
||||
local fields_seen=0
|
||||
for tag,port in pairs(machine().ioport.ports) do
|
||||
if #result.ports>=512 then result.truncated=true;break end
|
||||
local item={tag=tag,fields={}}
|
||||
for name,field in pairs(port.fields) do
|
||||
if fields_seen>=4096 then result.truncated=true;break end
|
||||
item.fields[#item.fields+1]={name=tostring(name),mask=field.mask}
|
||||
fields_seen=fields_seen+1
|
||||
end
|
||||
table.sort(item.fields,function(a,b) return a.mask<b.mask end)
|
||||
result.ports[#result.ports+1]=item
|
||||
end
|
||||
table.sort(result.ports,function(a,b) return a.tag<b.tag end)
|
||||
return result
|
||||
elseif command=="list_shares" then
|
||||
return share_info()
|
||||
elseif command=="read_share" then
|
||||
return read_share(args.tag,args.address,args.length)
|
||||
elseif command=="read_vram" then
|
||||
local matches={}
|
||||
for tag in pairs(machine().memory.shares) do
|
||||
if tag:find("vram",1,true) then matches[#matches+1]=tag end
|
||||
end
|
||||
assert(#matches==1,"Нужен ровно один VRAM share; используйте list_shares")
|
||||
return read_share(matches[1],args.address,args.length)
|
||||
elseif command=="screen_pixels" then
|
||||
local x=number(args.x,4095)
|
||||
local y=number(args.y,4095)
|
||||
local width=number(args.width,512)
|
||||
local height=number(args.height,512)
|
||||
assert(width>=1 and height>=1 and width*height<=8192,
|
||||
"Прямоугольник должен содержать 1..8192 пикселей")
|
||||
local screen=first_screen()
|
||||
local values={}
|
||||
for dy=0,height-1 do
|
||||
for dx=0,width-1 do
|
||||
values[#values+1]=string.format("%04X",screen:pixel(x+dx,y+dy)&0xffff)
|
||||
end
|
||||
end
|
||||
return {x=x,y=y,width=width,height=height,format="pen16be-hex",
|
||||
hex=table.concat(values),frame=screen:frame_number(),
|
||||
emulated_time=now(),stale_frame=state=="stopped",generation=generation}
|
||||
elseif command=="screen_snapshot" then
|
||||
assert(snapdir and #snapdir>0,"Каталог снимков не настроен")
|
||||
snapshot_sequence=snapshot_sequence+1
|
||||
local screen=first_screen()
|
||||
local path=string.format("%s/sdbg_%06d.png",snapdir,snapshot_sequence)
|
||||
local err=screen:snapshot(path)
|
||||
assert(err==nil,"Снимок не создан: "..tostring(err))
|
||||
local size=lfs.attributes(path,"size")
|
||||
assert(size and size>0 and size<=8388608,"Неверный размер снимка")
|
||||
return {path=path,size=size,format="png",frame=screen:frame_number(),
|
||||
emulated_time=now(),stale_frame=state=="stopped",generation=generation}
|
||||
elseif command=="console_print" then
|
||||
stopped(request)
|
||||
local text=args.text
|
||||
|
||||
Reference in New Issue
Block a user