#!/usr/bin/env bash # test.sh - headless test of a demo OMF. Boots MAME, drives the # Finder to launch the demo, waits for the demo to reach its event # loop, injects a keystroke to wake it, then reads back the # trailing marker at $00:0070 to confirm the demo ran to completion. # # Usage: bash demos/test.sh # # Expected outcome: $0070 reads back as $99. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" [ $# -ge 1 ] || { echo "usage: $0 " >&2; exit 2; } BASE="$1" SRC="$SCRIPT_DIR/$BASE.c" OMF="$SCRIPT_DIR/$BASE.omf" if [ ! -f "$OMF" ] || [ "$SRC" -nt "$OMF" ]; then bash "$SCRIPT_DIR/build.sh" "$BASE" >/dev/null fi CADIUS=${CADIUS:-$PROJECT_ROOT/tools/cadius/cadius} SYSDISK=${SYSDISK:-$PROJECT_ROOT/tools/gsos/sys602.po} [ -x "$CADIUS" ] || { echo "cadius not found: $CADIUS" >&2; exit 2; } [ -f "$SYSDISK" ] || { echo "sysdisk not found: $SYSDISK" >&2; exit 2; } command -v mame >/dev/null || { echo "mame not in PATH" >&2; exit 2; } WORK=$(mktemp -d -t demotest.XXXXXX) trap 'rm -rf "$WORK"' EXIT cp "$SYSDISK" "$WORK/disk.po" "$CADIUS" CREATEVOLUME "$WORK/data.po" DATA 800KB >/dev/null cp "$OMF" "$WORK/HELLO#B30000" "$CADIUS" ADDFILE "$WORK/data.po" /DATA "$WORK/HELLO#B30000" >/dev/null # Same Finder-navigation timeline as runViaFinder.sh, plus a key # injection at frame 6000 (well after the demo has launched and is # waiting in its event loop), and a memory check at frame 8000. cat > "$WORK/launch.lua" <<'LUA' local cpu = manager.machine.devices[":maincpu"] local mem = cpu.spaces["program"] local nat = manager.machine.natkeyboard local frame = 0 local idx = 1 local function get_field(port, name) local p = manager.machine.ioport.ports[port] if p == nil then return nil end return p.fields[name] end local key_cmd = get_field(":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 steps = { {3300, function() nat:post("D") end}, {3540, function() press(key_cmd) end}, {3546, function() nat:post("o") end}, {3600, function() release(key_cmd) end}, {4200, function() nat:post("H") end}, {4500, function() press(key_cmd) end}, {4506, function() nat:post("o") end}, {4560, function() release(key_cmd) end}, {7000, function() nat:post(" ") end}, -- wake the demo's event loop {7500, function() nat:post(" ") end}, -- send a few in case GetNextEvent runs in between {8000, function() nat:post(" ") end}, {9000, function() print(string.format("MAME-READ 0x70=%02x", mem:read_u8(0x70))) for a = 0x60, 0x7F do local v = mem:read_u8(a) if v ~= 0 and a ~= 0x70 then print(string.format("MAME-READ 0x%02x=%02x", a, v)) end end for a = 0x0F80, 0x0F9F do local v = mem:read_u8(a) if v ~= 0 then print(string.format("MAME-READ 0x%04x=%02x", a, v)) end end manager.machine:exit() 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 end) LUA OUT=$(timeout 200 mame apple2gs -rompath "$PROJECT_ROOT/tools/mame/roms" \ -window -nothrottle -sound none \ -seconds_to_run 180 -flop3 "$WORK/disk.po" -flop4 "$WORK/data.po" \ -autoboot_script "$WORK/launch.lua" &1) echo "$OUT" | grep "MAME-READ" || true VAL=$(echo "$OUT" | grep "MAME-READ 0x70=" | tail -1 | sed 's/.*=//') if [ "$VAL" = "99" ]; then echo "PASS: $BASE reached end (\$70 = 0x99)" exit 0 fi echo "FAIL: $BASE marker at \$70 = 0x$VAL (want 0x99)" exit 1