joeylib2/scripts/run-iigs-mame.sh

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