109 lines
3.7 KiB
Bash
Executable file
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
|