175 lines
6.7 KiB
Bash
Executable file
175 lines
6.7 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]}"
|
|
# 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
|