172 lines
7.2 KiB
Bash
Executable File
172 lines
7.2 KiB
Bash
Executable File
#!/bin/sh
|
|
# M2 end-to-end smoke test: TI ABI + a real ROM strlen call.
|
|
#
|
|
# scripts/m2/run.sh
|
|
#
|
|
# Compiles five C translation units with scripts/tms-clang + llc -O0, assembles
|
|
# and links them with the 1987 GSPA/GSPLNK 2.00 under DOSBox, then loads the
|
|
# COFF into MAME through dwt/loader/loader.lua and checks the marker words:
|
|
#
|
|
# check() strlen("0::0") == 4
|
|
# first(20,22) == 20
|
|
# second(20,22) == 22
|
|
# caller() add2(20,22) == 42
|
|
# locals() == 7
|
|
# keep(10) == 16
|
|
#
|
|
# Overridable: TMS_CLANG TMS_OPT TMS_BITADDR TMS_LLC MAME DOSBOX DOSBOX_CONF
|
|
# SDK_IMG M2_WORK M2_COFF M2_SDK LOADER_AT LOADER_END
|
|
set -eu
|
|
|
|
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
|
DIR=$ROOT/scripts/m2
|
|
|
|
LLVM_BIN=${LLVM_BIN:-$ROOT/build/llvm-clean-build/bin}
|
|
TMS_CLANG=${TMS_CLANG:-$LLVM_BIN/clang}
|
|
TMS_OPT=${TMS_OPT:-$LLVM_BIN/opt}
|
|
TMS_BITADDR=${TMS_BITADDR:-$ROOT/tools/tms-bitaddr/tms-bitaddr}
|
|
TMS_LLC=${TMS_LLC:-$LLVM_BIN/llc}
|
|
|
|
MAME=${MAME:-$ROOT/build/mame-src/vt1200}
|
|
DOSBOX=${DOSBOX:-dosbox}
|
|
DOSBOX_CONF=${DOSBOX_CONF:-$ROOT/vendor/vt1200/harness/sdk-scratch/dosbox-bench.conf}
|
|
SDK_IMG=${SDK_IMG:-$ROOT/vendor/vt1200/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img}
|
|
|
|
WORK=${M2_WORK:-$ROOT/build/m2}
|
|
COFF=${M2_COFF:-$WORK/m2.coff}
|
|
SDK=${M2_SDK:-$ROOT/build/m2-sdk}
|
|
ASM=$WORK/asm
|
|
LOADER_LUA=$ROOT/vendor/vt1200/dwt/loader/loader.lua
|
|
LOADER_BIN=$ROOT/vendor/vt1200/dwt/loader/build/loader.bin
|
|
LOADER_AT=${LOADER_AT:-2500}
|
|
LOADER_END=${LOADER_END:-2700}
|
|
|
|
SYMS=${M2_SYMS:-$ROOT/symbols/vt1200-v22.syms}
|
|
|
|
fail() {
|
|
echo "m2: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
for tool in "$TMS_CLANG" "$TMS_OPT" "$TMS_BITADDR" "$TMS_LLC"; do
|
|
[ -x "$tool" ] || fail "missing tool: $tool"
|
|
done
|
|
|
|
mkdir -p "$ASM" "$WORK/evidence" "$WORK/run/nvram" "$WORK/run/cfg"
|
|
mkdir -p "$(dirname "$COFF")"
|
|
|
|
echo "== compile and select"
|
|
export TMS_CLANG TMS_OPT TMS_BITADDR
|
|
for name in check args caller locals keep; do
|
|
"$ROOT/scripts/tms-clang" -O2 -o "$ASM/$name.ll" "$DIR/$name.c"
|
|
"$TMS_LLC" -march=tms34010 -O0 -filetype=asm \
|
|
"$ASM/$name.ll" -o "$ASM/$name.s" \
|
|
|| fail "llc failed for $name.c"
|
|
echo " $name.s: $(grep -c . "$ASM/$name.s") lines"
|
|
done
|
|
|
|
# strlen("0::0") must be a real ROM call, not a folded 4.
|
|
grep -q 'CALLA _strlen' "$ASM/check.s" \
|
|
|| fail "check.s has no 'CALLA _strlen' (strlen was folded or not called)"
|
|
echo " check.s: $(grep -E 'CALLA _strlen' "$ASM/check.s" | head -1 | sed 's/^[[:space:]]*//')"
|
|
|
|
echo "== generate the per-ROM import stubs"
|
|
"$DIR/gen-rom-imports.sh" "$SYMS" "$WORK/rom-imports.asm"
|
|
grep -q '^_strlen:' "$WORK/rom-imports.asm" \
|
|
|| fail "rom-imports.asm has no _strlen stub"
|
|
grep -A1 '^_strlen:' "$WORK/rom-imports.asm" | grep -qi 'jauc 3FB458D0h' \
|
|
|| fail "rom-imports.asm _strlen stub does not jump to 3FB458D0h"
|
|
echo " _strlen: $(grep -A1 '^_strlen:' "$WORK/rom-imports.asm" | tail -1 | sed 's/^[[:space:]]*//')"
|
|
|
|
echo "== extract the 1987 SDK"
|
|
if [ ! -f "$SDK/GSPA.EXE" ]; then
|
|
command -v mcopy >/dev/null 2>&1 || fail "mtools (mcopy) is required"
|
|
[ -f "$SDK_IMG" ] || fail "SDK image not found: $SDK_IMG"
|
|
mkdir -p "$SDK"
|
|
mcopy -s -n -i "$SDK_IMG" "::/A2/*" "$SDK/"
|
|
mcopy -s -n -i "$SDK_IMG" "::/C2/*" "$SDK/"
|
|
fi
|
|
[ -f "$SDK/GSPA.EXE" ] || fail "GSPA.EXE not extracted into $SDK"
|
|
|
|
echo "== assemble and link with GSPA/GSPLNK under DOSBox"
|
|
cat "$DIR/entry.asm" "$ASM/check.s" "$ASM/args.s" "$ASM/caller.s" \
|
|
"$ASM/locals.s" "$ASM/keep.s" "$WORK/rom-imports.asm" > "$SDK/M2.ASM"
|
|
# The linker command file must keep CRLF line endings.
|
|
sed 's/\r$//' "$DIR/link.cmd" | sed 's/$/\r/' > "$SDK/M2.CMD"
|
|
rm -f "$SDK/M2.OBJ" "$SDK/M2.OUT" "$SDK/M2.MAP" "$SDK/M2.LST"
|
|
|
|
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
|
timeout "${DOSBOX_TIMEOUT:-300}" "$DOSBOX" -conf "$DOSBOX_CONF" \
|
|
-noprimaryconf -nolocalconf \
|
|
-c "mount c $SDK" -c "c:" \
|
|
-c "gspa M2 M2.obj M2.lst -l" \
|
|
-c "gsplnk M2.cmd" \
|
|
-c "exit" -exit > "$WORK/dosbox.log" 2>&1 \
|
|
|| { tail -20 "$WORK/dosbox.log" >&2; fail "DOSBox failed"; }
|
|
|
|
[ -f "$SDK/M2.LST" ] || fail "GSPA produced no listing (see $WORK/dosbox.log)"
|
|
sed 's/\r$//' "$SDK/M2.LST" > "$WORK/m2.lst"
|
|
grep -q 'No Errors' "$WORK/m2.lst" \
|
|
|| { grep -i -B2 -A2 'error' "$WORK/m2.lst" >&2 || true; fail "GSPA reported errors"; }
|
|
grep -q 'No Warnings' "$WORK/m2.lst" \
|
|
|| { grep -i -B2 -A2 'warning' "$WORK/m2.lst" >&2 || true; fail "GSPA reported warnings"; }
|
|
[ -f "$SDK/M2.OUT" ] || fail "GSPLNK produced no M2.OUT"
|
|
sed 's/\r$//' "$SDK/M2.MAP" > "$WORK/m2.map"
|
|
cp "$SDK/M2.OUT" "$COFF"
|
|
cp "$SDK/M2.ASM" "$WORK/m2.asm"
|
|
|
|
ENTRY=$(awk '$2 == "_start" { print $1; exit }' "$WORK/m2.map")
|
|
[ "$ENTRY" = "018A0000" ] || fail "linker entry _start is '$ENTRY', want 018A0000"
|
|
grep -q 'ENTRY POINT SYMBOL: "_start"' "$WORK/m2.map" \
|
|
|| fail "linker map has no _start entry point"
|
|
|
|
BSS_ORIGIN=$(awk '$1 == ".bss" && $2 == "0" { print $3; exit }' "$WORK/m2.map")
|
|
BSS_LENGTH=$(awk '$1 == ".bss" && $2 == "0" { print $4; exit }' "$WORK/m2.map")
|
|
[ -n "$BSS_ORIGIN" ] || fail "linker map has no .bss section"
|
|
[ "$BSS_ORIGIN" = "018A9000" ] \
|
|
|| fail "linker .bss origin is $BSS_ORIGIN, want 018A9000 (link.cmd ram origin)"
|
|
[ "$BSS_LENGTH" = "00008000" ] \
|
|
|| fail ".bss is $BSS_LENGTH bits, expected 00008000 (_cstack)"
|
|
DATA_ORIGIN=$(awk '$1 == ".data" && $2 == "0" { print $3; exit }' "$WORK/m2.map")
|
|
DATA_LENGTH=$(awk '$1 == ".data" && $2 == "0" { print $4; exit }' "$WORK/m2.map")
|
|
[ -n "$DATA_LENGTH" ] || fail "linker map has no .data section"
|
|
[ "$DATA_LENGTH" != "00000000" ] || fail ".data is empty (no initialized globals)"
|
|
echo " linked .text at 018A0000, .data at $DATA_ORIGIN ($DATA_LENGTH bits), .bss at $BSS_ORIGIN ($BSS_LENGTH bits)"
|
|
|
|
echo "== run the COFF in MAME"
|
|
[ -x "$MAME" ] || fail "MAME binary not built: $MAME"
|
|
[ -f "$LOADER_LUA" ] || fail "COFF loader missing: $LOADER_LUA"
|
|
[ -f "$LOADER_BIN" ] || fail "COFF loader payload missing: $LOADER_BIN"
|
|
|
|
VT1200_SKIP_LEVEL1=1 \
|
|
LOADER_AT="$LOADER_AT" LOADER_END="$LOADER_END" \
|
|
LOADER_BIN="$LOADER_BIN" LOADER_COFF="$COFF" \
|
|
LOADER_OUT="$WORK/evidence" \
|
|
M2_LOADER_LUA="$LOADER_LUA" \
|
|
M2_EXPECT_CHECK=4 M2_EXPECT_FIRST=20 M2_EXPECT_SECOND=22 \
|
|
M2_EXPECT_CALLER=42 M2_EXPECT_LOCALS=7 M2_EXPECT_KEEP=16 \
|
|
timeout "${MAME_TIMEOUT:-600}" "$MAME" vt1200 \
|
|
-rompath "$ROOT/vendor/vt1200/harness/mame/roms" \
|
|
-video none -sound none -nothrottle -skip_gameinfo \
|
|
-nvram_directory "$WORK/run/nvram" \
|
|
-cfg_directory "$WORK/run/cfg" \
|
|
-debug -debugger none \
|
|
-autoboot_script "$DIR/m2-check.lua" -autoboot_delay 0 \
|
|
> "$WORK/mame.log" 2>&1 \
|
|
|| { tail -30 "$WORK/mame.log" >&2; fail "MAME run failed"; }
|
|
|
|
MARKERS=$(grep -E '^M2: check=' "$WORK/mame.log" | tail -1 || true)
|
|
[ -n "$MARKERS" ] || { tail -30 "$WORK/mame.log" >&2; fail "MAME produced no M2 markers"; }
|
|
echo " $MARKERS"
|
|
|
|
grep -q 'LOADER: result status=0 (OK)' "$WORK/mame.log" \
|
|
|| { grep 'LOADER: result' "$WORK/mame.log" >&2 || true; fail "COFF loader did not report status 0"; }
|
|
grep -q 'placed \.text .* 0 differing' "$WORK/mame.log" \
|
|
|| fail "COFF loader could not verify the placed .text bytes"
|
|
grep -q 'placed \.data .* 0 differing' "$WORK/mame.log" \
|
|
|| fail "COFF loader could not verify the placed .data bytes"
|
|
grep -q '^M2 MAME: PASS' "$WORK/mame.log" \
|
|
|| { tail -30 "$WORK/mame.log" >&2; fail "marker check failed"; }
|
|
|
|
echo "M2 MAME: PASS"
|