65816-llvm-mos/scripts/runInGno.sh
Scott Duensing 9e53e5fd38 GNO Support
2026-05-29 15:43:28 -05:00

171 lines
6.5 KiB
Bash
Executable file

#!/usr/bin/env bash
# runInGno.sh — run a llvm816-built shell command under real GNO/ME in MAME.
#
# Boots GS/OS 6.0.4 + GNO/ME 2.0.6, logs in as root, runs the given
# program at the gsh prompt, and polls bank-2 memory for a signature.
#
# Usage:
# runInGno.sh <program.omf> [--check addr=hexval ...] [--snapshots]
#
# <program.omf> a GS/OS-loadable OMF (NOT a flat link816 .bin).
# Build it through omfEmit — see demos/buildGno.sh.
# --check A=V after the program runs, assert mem[A] (16-bit) == V.
# --snapshots save PNGs of each boot/login/run stage to
# $GNO_SNAP_DIR (default /tmp/gnosnaps) for headless
# debugging.
#
# The program is placed on the disk as /bin/<name> (lowercased) and as
# /HELLO, so it can be invoked by bare name at the gsh prompt.
#
# Boot timeline (frame numbers tuned against MAME apple2gs; override
# via the GNO_*_FRAMES env vars if your MAME build differs):
# ~3600 GS/OS Finder ready, GNO + System Disk icons visible
# 3800 select GNO volume, Open Apple+O to open it
# 4600 select KERN, Open Apple+O to launch the GNO kernel
# ~16000 getty shows "login:" prompt
# 16200 type "root\n" (root has empty password)
# ~18000 gsh "% " prompt
# 18800 type "<name>\n" to run the program
# 19500+ poll bank-2 markers
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
PROG="${1:-}"
shift || true
[ -n "$PROG" ] || { echo "usage: $0 <program.omf> [--check A=V ...] [--snapshots]" >&2; exit 2; }
[ -f "$PROG" ] || { echo "program not found: $PROG" >&2; exit 2; }
CADIUS="$ROOT/tools/cadius/cadius"
SYSDISK="$ROOT/tools/gsos/6.0.4 - System.Disk.po"
BASE="$ROOT/tools/gno/gnobase.po"
[ -x "$CADIUS" ] || { echo "cadius missing" >&2; exit 1; }
[ -f "$SYSDISK" ] || { echo "GS/OS System Disk missing: $SYSDISK" >&2; exit 1; }
[ -f "$BASE" ] || { echo "GNO base disk missing — run tools/gno/buildDisk.sh" >&2; exit 1; }
SNAPSHOTS=0
CHECK_LUA=""
ADDR_LIST=(); EXPECT_LIST=()
while [ $# -gt 0 ]; do
case "$1" in
--snapshots) SNAPSHOTS=1; shift;;
--check)
shift
while [ $# -gt 0 ] && [[ "$1" != "--"* ]]; do
a="${1%=*}"; v="${1#*=}"
ADDR_LIST+=("$a"); EXPECT_LIST+=("$v")
shift
done;;
*) echo "unknown arg: $1" >&2; exit 2;;
esac
done
WORK=$(mktemp -d -t gno-run.XXXXXX)
trap 'rm -rf "$WORK"' EXIT
DATA="$WORK/data.po"
cp "$BASE" "$DATA"
NAME=$(basename "$PROG" | sed 's/\.[^.]*$//' | tr '[:upper:]' '[:lower:]' | cut -c1-15)
cp "$PROG" "$WORK/${NAME}#B50100"
cp "$PROG" "$WORK/HELLO#B50100"
"$CADIUS" ADDFILE "$DATA" /GNO.BOOT/bin "$WORK/${NAME}#B50100" >/dev/null
"$CADIUS" ADDFILE "$DATA" /GNO.BOOT "$WORK/HELLO#B50100" >/dev/null
# Optional: drop an extra TXT file in /home/root (the login cwd) for
# stdin-redirect tests. Reference it relatively or as /home/root/<name>.
if [ -n "${GNO_ADDFILE:-}" ] && [ -f "${GNO_ADDFILE}" ]; then
cp "${GNO_ADDFILE}" "$WORK/$(basename "${GNO_ADDFILE}")#040000"
"$CADIUS" ADDFILE "$DATA" /GNO.BOOT/home/root "$WORK/$(basename "${GNO_ADDFILE}")#040000" >/dev/null
fi
SNAPDIR="${GNO_SNAP_DIR:-/tmp/gnosnaps}"
if [ "$SNAPSHOTS" = 1 ]; then
mkdir -p "$SNAPDIR"; rm -rf "$SNAPDIR/apple2gs"/* 2>/dev/null || true
fi
# Frame tuning.
F_VOL=${GNO_VOL_FRAMES:-3800}
F_KERN=${GNO_KERN_FRAMES:-4600}
F_LOGIN=${GNO_LOGIN_FRAMES:-16200}
F_RUN=${GNO_RUN_FRAMES:-18800}
F_POLL=${GNO_POLL_FRAMES:-20000}
F_END=${GNO_END_FRAMES:-21000}
SECS=${MAME_SECS:-380}
# Optional: inject a line into the console after the program launches
# (for testing interactive stdin reads). GNO_STDIN is the text; it is
# posted with a trailing newline at frame F_RUN + GNO_STDIN_DELAY.
STDIN_STEP=""
if [ -n "${GNO_STDIN:-}" ]; then
STDIN_STEP="{$((F_RUN + ${GNO_STDIN_DELAY:-1600})), function() nat:post(\"${GNO_STDIN}\\n\") end},"
fi
# Build the bank-2 probe Lua for each --check.
for i in "${!ADDR_LIST[@]}"; do
CHECK_LUA="$CHECK_LUA print(string.format('MAME-READ addr=0x%06x val=0x%04x', ${ADDR_LIST[$i]}, mem:read_u16(${ADDR_LIST[$i]})))"$'\n'
done
LUA="$WORK/gno.lua"
cat > "$LUA" <<EOF
local nat = manager.machine.natkeyboard
local frame = 0
local idx = 1
local function gf(p, n) local x = manager.machine.ioport.ports[p]; return x and x.fields[n] end
local key_cmd = gf(":macadb:KEY3", "Command / Open Apple")
local function press(f) if f then f:set_value(1) end end
local function release(f) if f then f:set_value(0) end end
local snaps = $SNAPSHOTS
local function snap(t) if snaps == 1 then manager.machine.video:snapshot() end; print("MAME-STAGE "..t.." frame="..frame) end
local steps = {
{$F_VOL, function() nat:post("G") end},
{$F_VOL + 200, function() press(key_cmd) end},
{$F_VOL + 206, function() nat:post("o") end},
{$F_VOL + 260, function() release(key_cmd) end},
{$F_VOL + 400, function() snap("vol-open") end},
{$F_KERN, function() nat:post("K") end},
{$F_KERN + 200, function() press(key_cmd) end},
{$F_KERN + 206, function() nat:post("o") end},
{$F_KERN + 260, function() release(key_cmd) end},
{$F_LOGIN - 200,function() snap("login") end},
{$F_LOGIN, function() nat:post("root\n") end},
{$F_RUN - 200, function() snap("shell") end},
{$F_RUN, function() nat:post("${GNO_RUNCMD:-$NAME}\n") end},
${STDIN_STEP}
{$F_POLL, function() snap("running") end},
}
emu.register_frame_done(function()
frame = frame + 1
while idx <= #steps and frame >= steps[idx][1] do steps[idx][2](); idx = idx + 1 end
if frame == $F_POLL then
local mem = manager.machine.devices[":maincpu"].spaces["program"]
$CHECK_LUA
end
if frame > $F_END then manager.machine:exit() end
end)
EOF
OUT=$(SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout "${MAME_WALL:-120}" mame apple2gs \
-rompath "$ROOT/tools/mame/roms" \
-flop3 "$SYSDISK" -flop4 "$DATA" \
-snapshot_directory "$SNAPDIR" \
-autoboot_script "$LUA" \
-video none -sound none -nothrottle -seconds_to_run "$SECS" 2>&1)
echo "$OUT" | grep -E "^MAME-" || true
rc=0
for i in "${!ADDR_LIST[@]}"; do
a="${ADDR_LIST[$i]}"; exp="${EXPECT_LIST[$i]}"
got=$(echo "$OUT" | awk -v a="$a" 'index($0, "addr=" a) { print $NF }' | tail -1)
got="${got#val=}"
want="0x$(printf '%04x' "0x$exp")"
if [ "$got" = "$want" ]; then
echo "[llvm816] GNO check OK: $a = $got"
else
echo "[llvm816 FAIL] GNO check $a: expected $want got ${got:-(none)}"
rc=1
fi
done
exit $rc