#!/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 </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