203 lines
6.3 KiB
Bash
Executable file
203 lines
6.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Launch the IIgs sprite demo (or other example) under MAME's apple2gs
|
|
# driver instead of GSplus. We get:
|
|
# - The MAME debugger window (always visible with -debug)
|
|
# - debug.log file written to the working directory
|
|
# - A Lua hook that, when the CPU halts (e.g., BRK), dumps registers
|
|
# and the surrounding code bytes to /tmp/mame-iigs/crash.txt
|
|
#
|
|
# The boot path is the same as run-iigs.sh: gsos-system.po as flop3, our
|
|
# joey.2mg as flop4. Once Finder is up, navigate to JOEYLIB and double-
|
|
# click the example.
|
|
#
|
|
# Outputs of a run land in /tmp/mame-iigs/:
|
|
# - debug.log MAME's debugger console output (-debuglog)
|
|
# - crash.txt Lua-hook crash dump (if anything halts the CPU)
|
|
# - joeylog.txt Extracted from the post-run disk image
|
|
|
|
set -euo pipefail
|
|
|
|
prog=${1:-pattern}
|
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
|
case $prog in
|
|
hello|pattern|keys|joy|sprite|audio) ;;
|
|
*) echo "usage: $0 [hello|pattern|keys|joy|sprite|audio]" >&2; exit 2 ;;
|
|
esac
|
|
|
|
sys_disk=$repo/toolchains/emulators/support/gsos-system.po
|
|
data_disk=$repo/build/iigs/bin/joey.2mg
|
|
|
|
for f in "$sys_disk" "$data_disk"; do
|
|
if [[ ! -f $f ]]; then
|
|
echo "missing: $f" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
work=$(mktemp -d -t joeylib-mame.XXXXXX)
|
|
out=/tmp/mame-iigs
|
|
mkdir -p "$out"
|
|
|
|
cp "$sys_disk" "$work/boot.po"
|
|
cp "$data_disk" "$work/joey.2mg"
|
|
|
|
# Lua script: on every CPU stop (BRK, breakpoint, watchpoint, manual
|
|
# halt), append a state snapshot to crash.txt. This way we don't need
|
|
# the user to type anything at the debugger window -- whatever halts
|
|
# the CPU lands a record in crash.txt.
|
|
cat > "$work/crash-hook.lua" <<'LUA'
|
|
-- Crash diagnostics for IIgs demos. Auto-resumes the initial debug
|
|
-- pause so the user doesn't need to type "go". On any subsequent halt
|
|
-- (BRK, watchpoint, breakpoint) outside ROM, dumps registers + bytes
|
|
-- around PC to crash.txt. ROM halts (PB == 0xFE/0xFF) are skipped so
|
|
-- we don't fill the file with normal IIgs ROM stack walking.
|
|
local cpu = manager.machine.devices[":maincpu"]
|
|
local prog = cpu.spaces["program"]
|
|
local outpath = "/tmp/mame-iigs/crash.txt"
|
|
|
|
local function in_rom(pb)
|
|
return pb == 0xFE or pb == 0xFF
|
|
end
|
|
|
|
local function dump(label)
|
|
local f = io.open(outpath, "a")
|
|
if f == nil then return end
|
|
f:write(string.format("=== %s @ %s ===\n", label, os.date("%H:%M:%S")))
|
|
for k, v in pairs(cpu.state) do
|
|
f:write(string.format(" %s = %X\n", k, v.value))
|
|
end
|
|
local pc = cpu.state["CURPC"].value
|
|
local lo = (pc - 16) & 0xFFFFFF
|
|
f:write(string.format(" bytes %06X..%06X:", lo, lo + 32))
|
|
for i = 0, 32 do
|
|
local b = prog:read_u8(lo + i)
|
|
f:write(string.format(" %02X", b))
|
|
end
|
|
f:write("\n")
|
|
f:close()
|
|
end
|
|
|
|
-- Lua can't reliably terminate MAME from this version's API; instead
|
|
-- write a marker file and let the bash launcher poll for it and kill
|
|
-- the process. "done" file = launcher should shut down.
|
|
local done_marker = "/tmp/mame-iigs/.done"
|
|
local started = false
|
|
local crashed = false
|
|
local boot_frames = 0
|
|
local function signal_done(reason)
|
|
local f = io.open(done_marker, "w")
|
|
if f ~= nil then
|
|
f:write(reason)
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
emu.register_periodic(function()
|
|
local dbg = manager.machine.debugger
|
|
if dbg == nil then return end
|
|
if dbg.execution_state == "stop" then
|
|
if not started then
|
|
-- First halt is the -debug startup pause; auto-resume so
|
|
-- emulation begins without manual input.
|
|
started = true
|
|
dbg.execution_state = "run"
|
|
return
|
|
end
|
|
if not crashed then
|
|
crashed = true
|
|
dump("halt")
|
|
signal_done("halt")
|
|
end
|
|
else
|
|
boot_frames = boot_frames + 1
|
|
-- Watchdog: ~30 wall-sec at 60 Hz. If nothing crashes by
|
|
-- then, dump current state (likely the demo running fine)
|
|
-- and tell the launcher to shut down so we can grab joeylog.
|
|
if boot_frames > 1800 and not crashed then
|
|
crashed = true
|
|
dump("watchdog")
|
|
signal_done("watchdog")
|
|
end
|
|
end
|
|
end)
|
|
LUA
|
|
|
|
# Wipe prior crash.txt so we don't confuse runs.
|
|
: > "$out/crash.txt"
|
|
|
|
cat <<EOF
|
|
MAME apple2gs (auto-launch ${prog^^}):
|
|
Boot disk: $work/boot.po (flop3)
|
|
|
|
GS/OS will boot directly into ${prog^^}; no Finder navigation needed.
|
|
On crash the MAME debugger halts and Lua dumps state to:
|
|
$out/crash.txt
|
|
|
|
After exit:
|
|
$out/joeylog.txt extracted from disk image
|
|
$out/debug.log MAME debugger console output
|
|
|
|
EOF
|
|
|
|
cleanup() {
|
|
# Always rescue debug.log first -- it's written by MAME relative to
|
|
# cwd ($work) and lost when the dir is removed.
|
|
if [[ -f $work/debug.log ]]; then
|
|
mv -f "$work/debug.log" "$out/debug.log"
|
|
fi
|
|
local profuse=$repo/toolchains/iigs/gg-tools/bin/profuse
|
|
local mnt=$work/_mnt
|
|
if [[ -x $profuse && -f $work/joey.2mg ]]; then
|
|
export GOLDEN_GATE="$repo/toolchains/iigs/goldengate"
|
|
export ORCA_ROOT="$GOLDEN_GATE"
|
|
mkdir -p "$mnt"
|
|
if "$profuse" -oro "$work/joey.2mg" "$mnt" 2>/dev/null; then
|
|
for name in JOEYLOG.TXT joeylog.txt; do
|
|
if [[ -f $mnt/$name ]]; then
|
|
cp "$mnt/$name" "$out/joeylog.txt"
|
|
echo "extracted joeylog.txt -> $out/joeylog.txt" >&2
|
|
break
|
|
fi
|
|
done
|
|
fusermount -u "$mnt" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
rm -rf "$work"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Headless by default (-video none). Set MAME_WINDOW=1 to get a real
|
|
# emulator window for interactive use.
|
|
video_arg="-video none"
|
|
if [[ "${MAME_WINDOW:-0}" = "1" ]]; then
|
|
video_arg="-window"
|
|
fi
|
|
|
|
# Clear the done-marker the Lua hook uses to signal shutdown.
|
|
rm -f "$out/.done"
|
|
|
|
cd "$work"
|
|
mame apple2gs \
|
|
-flop3 "$work/boot.po" \
|
|
-flop4 "$work/joey.2mg" \
|
|
$video_arg -sound none \
|
|
-debug -debuglog \
|
|
-autoboot_script "$work/crash-hook.lua" &
|
|
mame_pid=$!
|
|
|
|
# Poll for the done-marker. Kill MAME once Lua signals it. Cap total
|
|
# wall-clock at 60 s in case MAME never writes the marker.
|
|
deadline=$((SECONDS + 60))
|
|
while kill -0 "$mame_pid" 2>/dev/null; do
|
|
if [[ -f $out/.done ]]; then
|
|
kill "$mame_pid" 2>/dev/null
|
|
break
|
|
fi
|
|
if (( SECONDS > deadline )); then
|
|
kill "$mame_pid" 2>/dev/null
|
|
break
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
wait "$mame_pid" 2>/dev/null || true
|