65816-llvm-mos/scripts/runInMameCycles.sh
Scott Duensing f542f4fa01 Checkpoint
2026-05-03 21:31:53 -05:00

109 lines
3.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# runInMameCycles.sh — measure emulated CPU time between START / DONE
# markers via MAME's emu.time().
#
# Usage: runInMameCycles.sh <binary> <iters>
# binary: 65816 image to load at $00:1000
# iters: number of bench iterations the binary ran (used to
# normalize delta to per-iteration cycles)
#
# The binary MUST:
# 1. Switch DBR to bank 2 (so the marker writes are observable
# at $025000 / $025002 — bank 0 there is also fine but harder
# to find atomically).
# 2. Write 0xA1A1 to $025000 *immediately before* the bench loop.
# 3. Write 0xA2A2 to $025002 *immediately after* the bench loop.
# 4. while(1){} after the DONE marker.
#
# Output (stdout):
# MAME-CYCLES iters=N delta_us=... cyc_per_call=... start_us=... done_us=...
# Exit 0 on success, 1 on time-out / missing markers.
#
# IIgs CPU clock rate. MAME's apple2gs starts in IIgs slow mode
# (1.023 MHz, IIe-compatible) until the IIgs ROM enables fast mode
# via $C036. We're booting our binary directly without going through
# the ROM, so we stay in slow mode unless the binary itself writes
# $80 to $C036. For the cycle harness we calibrate against slow
# mode (1023000 Hz) — both clang and Calypsi binaries run under
# the same emulator state, so the ratio is what matters. If you
# want fast-mode numbers, have the bench wrapper enable it.
set -euo pipefail
source "$(dirname "$0")/common.sh"
BIN="$1"
ITERS="${2:-100}"
SECS=10
CLOCK_HZ=1023000
[ -f "$BIN" ] || die "binary not found: $BIN"
LUA_PATH=$(mktemp --suffix=.lua)
trap 'rm -f "$LUA_PATH"' EXIT
cat > "$LUA_PATH" <<EOF
local frame = 0
local loaded = false
local start_t = nil
local done_t = nil
emu.register_frame_done(function()
frame = frame + 1
local cpu = manager.machine.devices[":maincpu"]
local mem = cpu.spaces["program"]
if frame == 30 and not loaded then
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
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)
return
end
if not loaded then return end
-- Poll markers on every frame after load. Capture emu.time()
-- the first frame each marker appears.
if not start_t and mem:read_u16(0x025000) == 0xa1a1 then
start_t = emu.time()
print(string.format("MAME-MARK START frame=%d t=%.9f", frame, start_t))
end
if start_t and not done_t and mem:read_u16(0x025002) == 0xa2a2 then
done_t = emu.time()
print(string.format("MAME-MARK DONE frame=%d t=%.9f", frame, done_t))
local delta = done_t - start_t
local delta_us = delta * 1e6
local cyc = delta * $CLOCK_HZ
local per_call = cyc / $ITERS
print(string.format("MAME-CYCLES iters=$ITERS delta_us=%.3f total_cyc=%.0f cyc_per_call=%.2f",
delta_us, cyc, per_call))
manager.machine:exit()
end
end)
EOF
OUT=$(timeout 60 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"
if echo "$OUT" | grep -q "MAME-CYCLES"; then
exit 0
fi
warn "no MAME-CYCLES output — markers not observed within $SECS sec"
exit 1