#!/usr/bin/env bash # Run a 65816 binary inside MAME's apple2gs simulation. # # Usage: # runInMame.sh # Read one 16-bit value at addr, compare to expected. # runInMame.sh --check = [= ...] # Read multiple 16-bit values, all must match. # runInMame.sh --check-u8 = [= ...] # Read multiple 8-bit (byte) values, all must match. Required by # SHR pixel probes (sprite/desktop work) where the unit of truth is # a single $E1:9D00..$E1:9FFF byte, not a 16-bit word. # # Addresses can be 24-bit (e.g., "0x025000" for bank 2 offset $5000). # Expected values are 4-hex (--check) or 2-hex (--check-u8), no 0x prefix. # # Code loads at $00:1000 in bank 0 RAM. Code can switch DBR to bank # 2+ for safe data writes (bank 0 zero page is scribbled by IIgs ROM # during execution). # # Exit 0 if all reads match, 1 otherwise. set -euo pipefail source "$(dirname "$0")/common.sh" BIN="$1" shift # Frame budget: load at frame 30, check at CHECK_FRAME (default 300 = 4.5 # simulated seconds after load). Override via env for heavy-compute tests. # Earlier default was 60 frames (0.5 sec), which falsely flagged slow but # correct math (e.g. 6-iter sqrt with chained soft-double libcalls) as # runtime hangs — see feedback_sqrt_runtime_broken.md. CHECK_FRAME=${MAME_CHECK_FRAME:-300} # seconds_to_run is simulated time; MAME terminates at this point. Sized # to comfortably exceed CHECK_FRAME (300 frames = 5 sec at 60Hz). SECS=${MAME_SECS:-6} # Build address list as Lua table entries. Two width modes: 16-bit # (default --check) and 8-bit (--check-u8). The width determines both # the Lua read function (read_u16 vs read_u8) and the printf format # (%04x vs %02x) so the post-run parser sees consistent widths. LUA_CHECKS="" EXPECT_LIST=() ADDR_LIST=() EXPECT_WIDTH=4 # hex digits per expected value if [ "$1" = "--check" ] || [ "$1" = "--check-u8" ]; then MODE="$1" shift if [ "$MODE" = "--check-u8" ]; then LUA_READ="mem:read_u8" LUA_FMT="%02x" EXPECT_WIDTH=2 else LUA_READ="mem:read_u16" LUA_FMT="%04x" fi 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$LUA_FMT', $ADDR, $LUA_READ($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 cat > "$LUA_PATH" <= 0x00C000 and addr < 0x00D000) then mem:write_u8(addr, data:byte(i)) end end 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 -- M=1, X=1, I=1 (IRQ off) cpu.state["E"].value = 0 cpu.state["S"].value = 0x01FF print("MAME-LOADED bytes=" .. #data) end if frame == $CHECK_FRAME then local cpu = manager.machine.devices[":maincpu"] local mem = cpu.spaces["program"] $LUA_CHECKS manager.machine:exit() end end) EOF OUT=$(SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout 30 mame apple2gs \ -rompath "$PROJECT_ROOT/tools/mame/roms" \ -plugins -autoboot_script "$LUA_PATH" \ -video none -sound none -nothrottle -seconds_to_run "$SECS" 2>&1 | grep "^MAME-") echo "$OUT" # Parse all val=... and compare to expected list. MAME's Lua prints # zero-padded lowercase hex (%02x for u8, %04x for u16); normalize the # user-supplied expected to the same width so callers can write "5" # instead of "05" for u8 probes. 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 want=$(printf "%0${EXPECT_WIDTH}x" "0x${EXPECT_LIST[$i]}" 2>/dev/null || printf '%s' "${EXPECT_LIST[$i]}") if [ "${GOT_LIST[$i]:-}" != "$want" ]; then warn "MAME mismatch at ${ADDR_LIST[$i]}: got 0x${GOT_LIST[$i]:-MISSING} expected 0x$want" ok=0 fi done if [ $ok -eq 1 ]; then log "MAME OK: ${#EXPECT_LIST[@]} reads matched" exit 0 fi exit 1