127 lines
3.9 KiB
Bash
Executable file
127 lines
3.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# runMultiSeg.sh — run a multi-segment program in MAME via a
|
|
# mini in-Lua loader. Reads the link816 manifest, loads each
|
|
# segment's image at its base address, sets PC to segment 1's
|
|
# entry, lets the program run, then reads check-address values.
|
|
#
|
|
# Usage: runMultiSeg.sh <manifest.json> [check args like runInMame.sh]
|
|
|
|
set -euo pipefail
|
|
source "$(dirname "$0")/common.sh"
|
|
|
|
MANIFEST="$1"
|
|
shift
|
|
SECS=3
|
|
|
|
# Build address list as Lua table entries, mirroring runInMame.sh.
|
|
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 "$MANIFEST" ] || die "manifest not found: $MANIFEST"
|
|
|
|
# Parse manifest with python (every machine has it). Emit a Lua
|
|
# table of (image_path, base, entry_offset_from_seg1).
|
|
PARSED=$(python3 - <<EOF
|
|
import json, os, sys
|
|
m = json.load(open("$MANIFEST"))
|
|
for s in m["segments"]:
|
|
base = int(s["base"], 16)
|
|
entry = int(s.get("entry_offset", "0x0"), 16) if s["num"] == 1 else 0
|
|
sz = s["size"]
|
|
print(f'{s["image"]}|{base}|{entry}|{sz}')
|
|
EOF
|
|
)
|
|
[ -n "$PARSED" ] || die "manifest parse failed"
|
|
|
|
LUA_PATH=$(mktemp --suffix=.lua)
|
|
trap 'rm -f "$LUA_PATH"' EXIT
|
|
|
|
# Build the per-segment load lua.
|
|
LOAD_LUA=""
|
|
ENTRY_BASE=0
|
|
ENTRY_OFF=0
|
|
while IFS='|' read -r img base entry sz; do
|
|
LOAD_LUA="$LOAD_LUA
|
|
do
|
|
local f = io.open('$img', 'rb')
|
|
if not f then print('SEG-MISSING $img'); manager.machine:exit(); return end
|
|
local data = f:read('*all'); f:close()
|
|
for i = 1, #data do
|
|
local addr = $base + i - 1
|
|
if not (addr >= 0x00C000 and addr < 0x00D000) then
|
|
mem:write_u8(addr, data:byte(i))
|
|
end
|
|
end
|
|
print('SEG-LOADED base=0x' .. string.format('%06x', $base) .. ' bytes=' .. #data)
|
|
end
|
|
"
|
|
if [ "$entry" != "0" ] || [ "$ENTRY_BASE" = "0" ]; then
|
|
ENTRY_BASE="$base"
|
|
ENTRY_OFF="$entry"
|
|
fi
|
|
done <<< "$PARSED"
|
|
|
|
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"]
|
|
$LOAD_LUA
|
|
loaded = true
|
|
cpu.state["PC"].value = $ENTRY_BASE + $ENTRY_OFF
|
|
cpu.state["PB"].value = ($ENTRY_BASE >> 16) & 0xff
|
|
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-READY pc=0x' .. string.format('%06x', $ENTRY_BASE + $ENTRY_OFF))
|
|
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 -E "^(MAME-|SEG-)")
|
|
|
|
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 (multi-seg) OK: ${#EXPECT_LIST[@]} reads matched"
|
|
exit 0
|
|
fi
|
|
exit 1
|