#!/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 [--check addr=hexval ...] [--snapshots] # # 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/ (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 "\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 [--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/. 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" <= 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]}" # MAME's Lua `string.format("%06x", ...)` lowercases hex digits, but a # user-supplied --check arg like `0x02500A` keeps the uppercase A. # Normalize both sides for a literal compare. aLower=$(echo "$a" | tr 'A-F' 'a-f') got=$(echo "$OUT" | awk -v a="$aLower" '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