65816-llvm-mos/scripts/runInMameWithGsosStub.sh
Scott Duensing c4da4b77b3 Checkpoint
2026-05-02 20:41:19 -05:00

122 lines
3.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Run a 65816 binary in MAME after pre-loading a stub GS/OS
# dispatcher at $E100A8. This lets the smoke test exercise the
# wrapper-to-dispatcher contract end-to-end without needing a
# real GS/OS-bootable disk image.
#
# The stub is the assembled bytes of /tmp/gsosStub.s (rebuilt
# from runtime/src/iigsGsosStub.s as part of this script — kept
# in-tree so it's reproducible). It writes byte $42 to *parm
# and returns A=0 (success) for any call number. That is enough
# to verify the wrapper:
# 1. pushes the parm-block low ptr (PHA),
# 2. pushes a zero bank (PEA 0),
# 3. JSLs $E100A8 with the right return convention,
# 4. returns A correctly to the caller.
#
# Usage: runInMameWithGsosStub.sh <binary> <addr> <expected>
# runInMameWithGsosStub.sh <binary> --check <addr>=<exp>...
set -euo pipefail
source "$(dirname "$0")/common.sh"
BIN="$1"
shift
SECS=3
# 23-byte stub bytes (see runtime/src/iigsGsosStub.s for source).
# Hand-assembled to avoid relying on llvm-mc tracking M-flag state.
STUB_HEX="0848 a309 85e4 a000 00e2 20a9 4291 e4c2 2068 28a9 0000 6b"
LUA_CHECKS=""
EXPECT_LIST=()
ADDR_LIST=()
if [ "$1" = "--check" ]; then
shift
for pair in "$@"; do
ADDR="${pair%=*}"
EXP="${pair#*=}"
ADDR_LIST+=("$ADDR")
EXPECT_LIST+=("$EXP")
LUA_CHECKS="$LUA_CHECKS print(string.format('MAME-READ addr=0x%06x val=0x%04x', $ADDR, mem:read_u16($ADDR)))"$'\n'
done
else
ADDR="$1"
EXP="$2"
ADDR_LIST+=("$ADDR")
EXPECT_LIST+=("$EXP")
LUA_CHECKS="print(string.format('MAME-READ addr=0x%06x val=0x%04x', $ADDR, mem:read_u16($ADDR)))"
fi
[ -f "$BIN" ] || die "binary not found: $BIN"
LUA_PATH=$(mktemp --suffix=.lua)
trap 'rm -f "$LUA_PATH"' EXIT
# Build the stub-write Lua statement (one mem:write_u8 per byte).
STUB_BYTES=$(echo "$STUB_HEX" | tr -d ' ')
STUB_LUA=""
i=0
while [ $i -lt ${#STUB_BYTES} ]; do
byte="${STUB_BYTES:$i:2}"
addr=$(( 0xe100a8 + i / 2 ))
STUB_LUA="${STUB_LUA} mem:write_u8(${addr}, 0x${byte})"$'\n'
i=$(( i + 2 ))
done
cat > "$LUA_PATH" <<EOF
local frame = 0
local loaded = false
emu.register_frame_done(function()
frame = frame + 1
if frame == 30 and not loaded then
local cpu = manager.machine.devices[":maincpu"]
local mem = cpu.spaces["program"]
local f = io.open("$BIN", "rb")
if not f then print("BIN-MISSING"); manager.machine:exit(); return end
local data = f:read("*all"); f:close()
for i = 1, #data do
local addr = 0x001000 + i - 1
if not (addr >= 0x00C000 and addr < 0x00D000) then
mem:write_u8(addr, data:byte(i))
end
end
-- Install GS/OS dispatcher stub at \$E100A8 (bank E1 RAM).
$STUB_LUA
loaded = true
cpu.state["PC"].value = 0x1000
cpu.state["PB"].value = 0x00
cpu.state["DB"].value = 0x00
cpu.state["D"].value = 0x00
cpu.state["P"].value = 0x34
cpu.state["E"].value = 0
cpu.state["S"].value = 0x01FF
print("MAME-LOADED bytes=" .. #data .. " stub=$((${#STUB_BYTES}/2))")
end
if frame == 60 then
local cpu = manager.machine.devices[":maincpu"]
local mem = cpu.spaces["program"]
$LUA_CHECKS
manager.machine:exit()
end
end)
EOF
OUT=$(timeout 30 mame apple2gs \
-rompath "$PROJECT_ROOT/tools/mame/roms" \
-plugins -autoboot_script "$LUA_PATH" \
-window -sound none -nothrottle -seconds_to_run "$SECS" 2>&1 | grep "^MAME-")
echo "$OUT"
mapfile -t GOT_LIST < <(printf '%s\n' "$OUT" | grep -oE 'val=0x[0-9a-f]+' | sed 's/val=0x//')
ok=1
for i in "${!EXPECT_LIST[@]}"; do
if [ "${GOT_LIST[$i]:-}" != "${EXPECT_LIST[$i]}" ]; then
warn "MAME mismatch at ${ADDR_LIST[$i]}: got 0x${GOT_LIST[$i]:-MISSING} expected 0x${EXPECT_LIST[$i]}"
ok=0
fi
done
if [ $ok -eq 1 ]; then
log "MAME (gsos-stub) OK: ${#EXPECT_LIST[@]} reads matched"
exit 0
fi
exit 1