neat-donk/tools/bsnes-launcher.lua

139 lines
4.4 KiB
Lua

local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1").."/.."
local util = dofile(base.."/util.lua")
local config = dofile(base.."/config.lua")
local mem = dofile(base.."/mem.lua")
local function text(text)
io.stderr:write(text)
io.stderr:write('\n')
print(text)
end
local function help()
text([[
Syntax: BSNES_LAUNCHER_ARGS='<arguments>' lsnes --lua=]]..base..[[/bsnes-launcher.lua
--sprite-startindex=<number> Which sprite to start at. Can be a value from 0-22
Sprite breakpoint arguments:
These will create breakpoints for all sprite slots with properties matching the
given pattern.
]])
for propName,_ in pairs(mem.offset.sprite) do
text('--sprite-'..propName..'<Breakpoint format>')
end
text([[
Breakpoint format: --<switchname>[=<value>][:<rwx>]
rwx = read / write / execute flags
For example, --sprite-x:r would match any reads of any sprite X position
--sprite-x>10:w would match any writes of any sprite X position greater
than 0x10 (16). Omitting the rwx will create the breakpoints with the values
specified but they will not trigger until you enable them manually.
]])
end
local bps = {}
--- Add breakpoint switch
---@param switchName string The name of the switch, without dashes
---@param source string address space
---@param startAddress integer The start address
---@param arg string The argument to test
---@param valWidth integer byte size of value - defaults to 2
---@param addrWidth integer byte size of address - defaults to 3
local function bpSwitch(switchName, source, startAddress, arg, valWidth, addrWidth)
if addrWidth == nil then
addrWidth = 3
end
if valWidth == nil then
valWidth = 2
end
if arg:sub(1, #switchName+2) == '--'..switchName then
local op, valHex, rwx = arg:sub(#switchName+3):match('^([><=]*)([0-9a-fA-F]*):?([rwxRWX]*)$')
local valPad = ''
if valHex ~= '' then
local val = tonumber(valHex, 16)
valPad = string.format('%0'..(valWidth*2)..'x', val)
end
local fmt = '%0'..(addrWidth*2)..'x%s%s:%s:%s'
if valWidth == 2 then
-- SNES is LE!
local valLeast = valPad:sub(3, 4)
local valMost = valPad:sub(1, 2)
local opLeast = ''
local opMost = ''
-- FIXME This is surely wrong but better than nothing?
if op == '>' then
opLeast = '>'
opMost = '>='
elseif op == '=' then
opLeast = '='
opMost = '='
elseif op == '<' then
opLeast = '<'
opMost = '<='
elseif op == '<=' then
opLeast = '<='
opMost = '<='
elseif op == '>=' then
opLeast = '>='
opMost = '>='
else
valLeast = ''
valMost = ''
end
table.insert(bps, string.format(fmt, startAddress, opLeast, valLeast, rwx, source))
table.insert(bps, string.format(fmt, startAddress + 1, opMost, valMost, rwx, source))
elseif valWidth == 1 then
table.insert(bps, string.format(fmt, startAddress, op, valPad, rwx, source))
end
end
end
local count = 0
local startIndex = 0
for arg in os.getenv('BSNES_LAUNCHER_ARGS'):gmatch('[^ ]+') do
if arg:sub(1,20) == '--sprite-startindex=' then
startIndex = tonumber(arg:sub(21))
end
count = count + 1
for propName,offset in pairs(mem.offset.sprite) do
for i=startIndex,22,1 do
local startAddress = mem.addr.spriteBase + mem.size.sprite * i + offset
bpSwitch('sprite-'..propName, 'cpu', startAddress, arg)
end
end
end
local cmdFmt = 'bsnes %s--show-debugger "%s"'
local bpArgs = ''
local withoutBps = string.format(cmdFmt, bpArgs, config.ROM)
if #bps > 0 then
bpArgs = '-b "'..table.concat(bps, '" -b "')..'" '
end
local withBps = string.format(cmdFmt, bpArgs, config.ROM)
if count == 0 then
text('====================')
help()
end
text('====================')
text('Note that you will need to turn off breakpoint saving for this app to work correctly.')
text('')
text(withBps)
text('====================')
while true do
text('Starting without breakpoints first. Save your state where you want with F2, then quit to load with breakpoints enabled')
util.doCmd(withoutBps)
text('Starting with breakpoints enabled. Load your state with F4')
util.doCmd(withBps)
end