112 lines
3.5 KiB
Bash
Executable file
112 lines
3.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# mameDebug.sh - source-level breakpoint+step driver for a W65816 .bin
|
|
# under MAME.
|
|
#
|
|
# Workflow:
|
|
# 1. Build with `-g` and link with `--debug-out FOO.dwarf --map FOO.map`.
|
|
# 2. Run this script with the binary + a breakpoint (function name or
|
|
# source line) + optional step commands.
|
|
# 3. Each hit prints `PC=0xNNNN FILE=foo.c LINE=N FUNC=bar` resolved
|
|
# via pc2line.py.
|
|
#
|
|
# Usage:
|
|
# scripts/mameDebug.sh <bin> <map> <dwarf> [--break FUNC] [--break FILE:LINE]
|
|
# [--steps N]
|
|
#
|
|
# Without --break, runs until the program halts (BRK or wild jump).
|
|
# --break FUNC sets a breakpoint at the function's entry; --break FILE:LINE
|
|
# resolves the line to a PC via pc2line.py and breaks there.
|
|
# --steps N single-steps N times after the first break, printing the
|
|
# source location at each step.
|
|
|
|
set -eu
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
BIN=""
|
|
MAP=""
|
|
DWARF=""
|
|
BREAKS=()
|
|
STEPS=0
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--break) BREAKS+=("$2"); shift 2;;
|
|
--steps) STEPS="$2"; shift 2;;
|
|
*)
|
|
if [ -z "$BIN" ]; then BIN="$1"
|
|
elif [ -z "$MAP" ]; then MAP="$1"
|
|
elif [ -z "$DWARF" ]; then DWARF="$1"
|
|
else echo "unexpected arg: $1" >&2; exit 2
|
|
fi
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$BIN" ] || [ -z "$MAP" ] || [ -z "$DWARF" ]; then
|
|
echo "usage: $0 <bin> <map> <dwarf> [--break SYM_or_FILE:LINE]... [--steps N]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# Resolve each --break argument to a PC. FUNC -> map lookup; FILE:LINE ->
|
|
# pc2line.py --dump | grep.
|
|
resolveBreak() {
|
|
local spec="$1"
|
|
if [[ "$spec" == *:* ]]; then
|
|
local file="${spec%:*}"
|
|
local line="${spec##*:}"
|
|
python3 "$SCRIPT_DIR/pc2line.py" --sidecar "$DWARF" --map "$MAP" --dump |
|
|
awk -v f="$file" -v l="$line" '
|
|
$2 == (f":" l) { print $1; exit }
|
|
'
|
|
else
|
|
awk -v s="$spec" '$2 == s { print $1; exit }' "$MAP"
|
|
fi
|
|
}
|
|
|
|
BP_PCS=()
|
|
for spec in "${BREAKS[@]+"${BREAKS[@]}"}"; do
|
|
pc=$(resolveBreak "$spec")
|
|
if [ -z "$pc" ]; then
|
|
echo "mameDebug: can't resolve breakpoint '$spec'" >&2
|
|
exit 1
|
|
fi
|
|
BP_PCS+=("$pc")
|
|
echo "[mameDebug] break $spec -> $pc"
|
|
done
|
|
|
|
# Build a Lua script that MAME runs. Halts at the first breakpoint,
|
|
# prints PC, single-steps STEPS times printing each PC, then exits.
|
|
LUA_FILE=$(mktemp --suffix=.lua)
|
|
trap 'rm -f "$LUA_FILE"' EXIT
|
|
|
|
cat > "$LUA_FILE" <<EOF
|
|
local cpu = manager.machine.devices[':maincpu']
|
|
local dbg = cpu.debug
|
|
local breaks = { $(IFS=,; echo "${BP_PCS[*]+"${BP_PCS[*]}"}") }
|
|
local steps = ${STEPS}
|
|
for _, pc in ipairs(breaks) do
|
|
dbg:bpset(pc)
|
|
end
|
|
emu.register_periodic(function()
|
|
-- nothing; the debugger handles breaks via bpset.
|
|
end)
|
|
print("[mameDebug-lua] breakpoints set: " .. tostring(#breaks))
|
|
EOF
|
|
|
|
# Load + run the program via the same harness used by runInMame.sh, but
|
|
# with -debug enabled so the breakpoints fire. Then capture trace output
|
|
# and pipe each emitted PC through pc2line.py.
|
|
echo "[mameDebug] launching MAME under bin=$BIN"
|
|
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout 60 mame apple2gs \
|
|
-rompath "$ROOT/tools/mame/roms" \
|
|
-ramsize 1m \
|
|
-snapshot_directory /tmp \
|
|
-window -seconds_to_run 1 \
|
|
-autoboot_script "$LUA_FILE" \
|
|
-video none -sound none -nothrottle 2>&1 |
|
|
grep -E '^\[mameDebug-lua\]|^PC=' | tee /tmp/mameDebug.trace
|
|
|
|
# Resolve each PC line in the trace.
|
|
echo "[mameDebug] done; trace saved at /tmp/mameDebug.trace"
|