mirror of
https://github.com/holub/mame
synced 2025-06-08 22:03:55 +03:00

More Lua interface cleanup, additional properties and methods, and documentation migration/expansion. Emulated switch inputs can have "not" codes applied to host input axis directions. It works the same way as host switch inputs - push twice for a "not" prefix. Input polling helpers no longer need to store state in the input device items. There’s less leakage, and less chance of things interfering with each other. Allow snapshot view options to be configured through the internal UI via the video options menu. Made video options menus place initial focus on the currently selected view item. Removed some crud from the menu base class. Fixed the description of the "snapview" option. The value to get raw screen pixels was changed to "native" a long time ago but the description was never updated. Re-arranged the Golden Poker button lamps so that the 6-button layouts for Jolli Witch and Wild Witch make sense. In 6-button mode, the hold buttons double as bonus game and bet buttons, but the lamp outputs don't change. The simplest way to deal with this without requiring the user to switch views or using layout scripting is to place the dedicated buttons directly below the hold buttons that correspond to them. Removed some software list data that was redundantly copied into device_image_interface (m_supported was never even set, so it didn't even work), and made crc() work better (previously it wasn't recalculuated after unloading and loading another image). Made strformat.h and devcb.h play nicer with C++17 and pre-standard C++20. Format precision now correctly limits the length of string views. Confirmed that strformat.{h,cpp} works with pre-standard C++20 support in GCC 9. Removed an auto_alloc from cpu/arm7.
290 lines
7.6 KiB
Lua
290 lines
7.6 KiB
Lua
-- 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:readv_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:writev_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
|