diff --git a/plugins/cheat/init.lua b/plugins/cheat/init.lua index dd4186cf1be..e9150e1bda4 100644 --- a/plugins/cheat/init.lua +++ b/plugins/cheat/init.lua @@ -82,6 +82,7 @@ function cheat.startplugin() local cheatname = "" local consolelog = nil local consolelast = 0 + local perodicset = false local watches = {} local breaks = {} @@ -255,11 +256,13 @@ function cheat.startplugin() end local function periodiccb() + local last = consolelast local msg = consolelog[#consolelog] - if #consolelog > consolelast and msg:find("Stopped at", 1, true) then - local point = msg:match("Stopped at breakpoint ([0-9]+)") + consolelast = #consolelog + if #consolelog > last and msg:find("Stopped at", 1, true) then + local point = tonumber(msg:match("Stopped at breakpoint ([0-9]+)")) if not point then - point = msg:match("Stopped at watchpoint ([0-9]+") + point = tonumber(msg:match("Stopped at watchpoint ([0-9]+")) if not point then return -- ?? end @@ -270,15 +273,14 @@ function cheat.startplugin() -- don't use an b/wpset action because that will supress the b/wp index manager:machine():debugger().execution_state = "run" end - return - end - local bp = breaks[point] - if bp then - run_if(bp.cheat, bp.func) - manager:machine():debugger().execution_state = "run" + else + local bp = breaks[point] + if bp then + run_if(bp.cheat, bp.func) + manager:machine():debugger().execution_state = "run" + end end end - consolelast = #consolelog end local function bpset(cheat, dev, addr, func) @@ -364,7 +366,10 @@ function cheat.startplugin() wpset = function(space, wptype, addr, len, func) wpset(cheat, dev, space, wptype, addr, len, func) end } cheat.bp = {} cheat.wp = {} - emu.register_periodic(periodic_cb) + if not periodicset then + emu.register_periodic(periodic_cb) + periodicset = true + end end end end diff --git a/plugins/gdbstub/init.lua b/plugins/gdbstub/init.lua new file mode 100644 index 00000000000..f463d816a35 --- /dev/null +++ b/plugins/gdbstub/init.lua @@ -0,0 +1,289 @@ +-- license:BSD-3-Clause +-- copyright-holders: Carl +local exports = {} +exports.name = "gdbstub" +exports.version = "0.0.1" +exports.description = "GDB stub plugin" +exports.license = "The BSD 3-Clause License" +exports.author = { name = "Carl" } + +local gdbstub = exports + +-- percpu mapping of mame registers to gdb register order +local regmaps = { + i386 = { + togdb = { + EAX = 1, ECX = 2, EDX = 3, EBX = 4, ESP = 5, EBP = 6, ESI = 7, EDI = 8, EIP = 9, EFLAGS = 10, CS = 11, SS = 12, + DS = 13, ES = 14, FS = 15, GS = 16 }, + fromgdb = { + "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI", "EIP", "EFLAGS", "CS", "SS", "DS", "ES", "FS", "GS" }, + regsize = 4, + addrsize = 4, + pcreg = "EIP" + } +} +regmaps.i486 = regmaps.i386 +regmaps.pentium = regmaps.i386 + +function gdbstub.startplugin() + local debugger + local debug + local cpu + local breaks + local watches + local consolelog + local consolelast + local running + + emu.register_start(function () + debugger = manager:machine():debugger() + if not debugger then + print("gdbstub: debugger not enabled") + return + end + cpu = manager:machine().devices[":maincpu"] + if not cpu then + print("gdbstub: maincpu not found") + end + if not regmaps[cpu:shortname()] then + print("gdbstub: no register map for cpu " .. cpu:shortname()) + cpu = nil + end + consolelog = debugger.consolelog + consolelast = 0 + breaks = {byaddr = {}, byidx = {}} + watches = {byaddr = {}, byidx = {}} + running = false + end) + + emu.register_stop(function() + consolelog = nil + cpu = nil + debug = nil + end) + + local socket = emu.file("", 7) + local connected = false + socket:open("socket.127.0.0.1:2159") + + emu.register_periodic(function () + if not cpu then + return + end + + if running and debugger.execution_state == "stop" then + socket:write("$S05#B8") + running = false + return + elseif debugger.execution_state == "run" then + running = true + end + + local function chksum(str) + local sum = 0 + str:gsub(".", function(s) sum = sum + s:byte() end) + return string.format("%.2x", sum & 0xff) + end + + local function makebestr(val, len) + local str = "" + for count = 0, len - 1 do + str = str .. string.format("%.2x", (val >> (count * 8)) & 0xff) + end + return str + end + + local last = consolelast + local msg = consolelog[#consolelog] + consolelast = #consolelog + if #consolelog > last and msg:find("Stopped at", 1, true) then + local point = tonumber(msg:match("Stopped at breakpoint ([0-9]+)")) + local map = regmaps[cpu:shortname()] + running = false + if not point then + point = tonumber(msg:match("Stopped at watchpoint ([0-9]+")) + if not point then + return -- ?? + end + local wp = watches.byidx[point] + if wp then + local reply = "T05" .. wp.type .. ":" .. makebestr(wp.addr, map.addrsize) + socket:write("$" .. reply .. "#" .. chksum(reply)) + else + socket:write("$S05#B8") + end + return + else + local bp = breaks.byidx[point] + if bp then + local reply = "T05hwbreak:" .. makebestr(cpu.state[map.pcreg].value, map.regsize) + socket:write("$" .. reply .. "#" .. chksum(reply)) + else + socket:write("$S05#B8") + end + return + end + end + + if running and debugger.execution_state == "stop" then + socket:write("$S05#B8") + running = false + return + elseif debugger.execution_state == "run" then + running = true + end + + local data = "" + + repeat + local read = socket:read(100) + data = data .. read + until #read == 0 + if #data == 0 then + return + end + if data == "\x03" then + debugger.execution_state = "stop" + socket:write("$S05#B8") + running = false + return + end + local packet, checksum = data:match("%$([^#]+)#(%x%x)") + if packet then + packet:gsub("}(.)", function(s) return string.char(string.byte(s) ~ 0x20) end) + local cmd = packet:sub(1, 1) + local map = regmaps[cpu:shortname()] + if cmd == "g" then + local regs = {} + for reg, idx in pairs(map.togdb) do + regs[idx] = makebestr(cpu.state[reg].value, map.regsize) + end + local data = table.concat(regs) + socket:write("+$" .. data .. "#" .. chksum(data)) + elseif cmd == "G" then + local count = 0 + packet:sub(2):gsub(string.rep("%x", map.regsize * 2), function(s) + count = count + 1 + cpu.state[map.fromgdb[count]].value = tonumber(s,16) + end) + socket:write("+$OK#9a") + elseif cmd == "m" then + local addr, len = packet:match("m(%x+),(%x+)") + if addr and len then + addr = tonumber(addr, 16) + len = tonumber(len, 16) + local data = "" + local space = cpu.spaces["program"] + for count = 1, len do + data = data .. string.format("%.2x", space:read_log_u8(addr)) + addr = addr + 1 + end + socket:write("+$" .. data .. "#" .. chksum(data)) + else + socket:write("+$E00#a5") -- fix error + end + elseif cmd == "M" then + local count = 0 + local addr, len, data = packet:match("M(%x+),(%x+),(%x+)") + if addr and len and data then + addr = tonumber(addr, 16) + local space = cpu.spaces["program"] + data:gsub("%x%x", function(s) space:write_log_u8(addr + count, tonumber(s, 16)) count = count + 1 end) + socket:write("+$OK#9a") + else + socket:write("+$E00#a5") + end + elseif cmd == "s" then + if #packet == 1 then + cpu:debug():step() + socket:write("+$OK#9a") + socket:write("$S05#B8") + running = false + else + socket:write("+$E00#a5") + end + elseif cmd == "c" then + if #packet == 1 then + cpu:debug():go() + socket:write("+$OK#9a") + else + socket:write("+$E00#a5") + end + elseif cmd == "Z" then + local btype, addr, kind = packet:match("Z([0-4]),(%x+),(.*)") + addr = tonumber(addr, 16) + if btype == "0" then + socket:write("") -- is machine dependant + elseif btype == "1" then + if breaks.byaddr[addr] then + socket:write("+$E00#a5") + return + end + local idx = cpu:debug():bpset(addr) + breaks.byaddr[addr] = idx + breaks.byidx[idx] = addr + socket:write("+$OK#9a") + elseif btype == "2" then + if watches.byaddr[addr] then + socket:write("+$E00#a5") + return + end + local idx = cpu:debug():wpset(cpu.spaces["program"], "w", addr, 1) + watches.byaddr[addr] = idx + watches.byidx[idx] = {addr = addr, type = "watch"} + socket:write("+$OK#9a") + elseif btype == "3" then + if watches.byaddr[addr] then + socket:write("+$E00#a5") + return + end + local idx = cpu:debug():wpset(cpu.spaces["program"], "r", addr, 1) + watches.byaddr[addr] = idx + watches.byidx[idx] = {addr = addr, type = "rwatch"} + socket:write("+$OK#9a") + elseif btype == "4" then + if watches.byaddr[addr] then + socket:write("+$E00#a5") + return + end + local idx = cpu:debug():wpset(cpu.spaces["program"], "rw", addr, 1) + watches.byaddr[addr] = idx + watches.byidx[idx] = {addr = addr, type = "awatch"} + socket:write("+$OK#9a") + end + elseif cmd == "z" then + local btype, addr, kind = packet:match("z([0-4]),(%x+),(.*)") + addr = tonumber(addr, 16) + if btype == "0" then + socket:write("") -- is machine dependent + elseif btype == "1" then + if not breaks.byaddr[addr] then + socket:write("+$E00#a5") + return + end + local idx = breaks.byaddr[addr] + cpu:debug():bpclr(idx) + breaks.byaddr[addr] = nil + breaks.byidx[idx] = nil + socket:write("+$OK#9a") + elseif btype == "2" or btype == "3" or btype == "4" then + if not watches.byaddr[addr] then + socket:write("+$E00#a5") + return + end + local idx = watches.byaddr[addr] + cpu:debug():wpclr(idx) + watches.byaddr[addr] = nil + watches.byidx[idx] = nil + socket:write("+$OK#9a") + end + elseif cmd == "?" then + socket:write("+$S05#B8") + else + socket:write("+$#00") + end + end + end) +end + +return exports diff --git a/plugins/gdbstub/plugin.json b/plugins/gdbstub/plugin.json new file mode 100644 index 00000000000..84c2d163b74 --- /dev/null +++ b/plugins/gdbstub/plugin.json @@ -0,0 +1,10 @@ +{ + "plugin": { + "name": "gdbstub", + "description": "GDB stub plugin", + "version": "0.0.1", + "author": "Carl", + "type": "plugin", + "start": "false" + } +}