Files
tms-c-compiler/scripts/m3/run.sh
T

224 lines
9.7 KiB
Bash
Executable File

#!/bin/sh
# M3 end-to-end test: compiled branches/loops plus the first compiled C Xlib
# client, linked with the 1987 GSPA/GSPLNK toolchain and run in MAME.
#
# scripts/m3/run.sh
#
# Compiles xhello.c, branch.c and layout.c with scripts/tms-clang -O2 and
# llc -O0, assembles/links them with the SDK under DOSBox, loads the COFF
# through dwt/loader/loader.lua, and checks:
#
# 018A8000 Display pointer (nonzero)
# 018A8020 root window id (nonzero)
# 018A8040 window id (nonzero)
# 018A8060 XSync result (diagnostic only)
# 018A8080 sumto(10) == 55
# 018A80A0 max2(20,22)== 22
# 018A80C0 lt(5,7) == 1
# 018A80E0 layout_screens() == 0x480
# 018A8100 layout_root() == 0x40
#
# and, decisively, the full all-white VRAM word at (150,150) and another at
# (250,200) inside the mapped window. A markers-only or root-window bit is a
# failure.
#
# Overridable: TMS_CLANG TMS_OPT TMS_BITADDR TMS_LLC MAME DOSBOX DOSBOX_CONF
# SDK_IMG M3_WORK M3_COFF M3_SDK LOADER_AT LOADER_END
set -eu
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
DIR=$ROOT/scripts/m3
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=${M3_WORK:-$ROOT/build/m3}
COFF=${M3_COFF:-$WORK/m3.coff}
SDK=${M3_SDK:-$ROOT/build/m3-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:-3400}
SYMS=${M3_SYMS:-$ROOT/symbols/vt1200-v22.syms}
fail() {
echo "m3: $*" >&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")"
# IndVarSimplify's exit-value replacement turns sumto's loop into an i33
# multiply (n*(n+1)/2), and this target has no multiply or i64 support: llc
# then dies selecting umul_lohi. Keep the loop (which is the point of the M3
# branch test) by disabling only that rewrite, and only for branch.c; xhello
# and layout use the normal optimizer pipeline. branch.c itself is unchanged.
OPT_NOREPLEXIT=$WORK/opt-keep-loops
printf '#!/bin/sh\nexec "%s" -replexitval=never "$@"\n' "$TMS_OPT" > "$OPT_NOREPLEXIT"
chmod +x "$OPT_NOREPLEXIT"
echo "== compile and select"
export TMS_CLANG TMS_BITADDR
for name in xhello branch layout; do
opt=$TMS_OPT
[ "$name" = branch ] && opt=$OPT_NOREPLEXIT
TMS_OPT=$opt "$ROOT/scripts/tms-clang" -O2 -I "$DIR" -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
# The Xlib calls must be real ROM imports, and the window must use background
# pixel 1 (white).
grep -q 'CALLA _XOpenDisplay' "$ASM/xhello.s" \
|| fail "xhello.s has no 'CALLA _XOpenDisplay'"
grep -q 'CALLA _XCreateSimpleWindow' "$ASM/xhello.s" \
|| fail "xhello.s has no 'CALLA _XCreateSimpleWindow'"
grep -q 'CALLA _XMapWindow' "$ASM/xhello.s" \
|| fail "xhello.s has no 'CALLA _XMapWindow'"
grep -q 'CALLA _XSync' "$ASM/xhello.s" \
|| fail "xhello.s has no 'CALLA _XSync'"
# 9 arguments: background=1, border=0, width=2, height=200, width=400, y=100,
# x=100, root, display. The first argument (background) is the last pushed,
# i.e. the first MOVI before the nine '*A14+' stores.
PUSHES=$(awk '
/MOVE .*, *\*A14\+, *1/ { n++ }
/CALLA/ { if ($0 ~ /_XCreateSimpleWindow/) print n; n = 0 }
' "$ASM/xhello.s")
[ "$PUSHES" = "9" ] || fail "XCreateSimpleWindow has $PUSHES pushes, want 9"
grep -q 'MOVI 1, ' "$ASM/xhello.s" \
|| fail "xhello.s has no background pixel 1 (white)"
echo " XCreateSimpleWindow: 9 argument pushes, background pixel 1"
echo "== generate the per-ROM import stubs"
"$ROOT/scripts/m2/gen-rom-imports.sh" "$SYMS" "$WORK/rom-imports.asm"
for sym in XOpenDisplay XCreateSimpleWindow XMapWindow XSync; do
grep -q "^_$sym:" "$WORK/rom-imports.asm" \
|| fail "rom-imports.asm has no _$sym stub"
done
grep -A1 '^_XOpenDisplay:' "$WORK/rom-imports.asm" | grep -qi 'jauc 3F9C2570h' \
|| fail "rom-imports.asm _XOpenDisplay stub does not jump to 3F9C2570h"
echo " _XOpenDisplay: $(grep -A1 '^_XOpenDisplay:' "$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"
# GSPA 2.00 has one flat symbol table per assembly, and llc names basic blocks
# LBB0_1, LBB1_2, ... restarting in every translation unit. Concatenating the
# three TUs would therefore collide (DUPLICATE DEFINITION). Give each TU its
# own LBB prefix; the labels stay LBB… (GSPA rejects dotted .L labels).
for pair in branch:B layout:L xhello:X; do
src=${pair%%:*}
tag=${pair##*:}
sed "s/LBB\([0-9][0-9]*_[0-9][0-9]*\)/LBB$tag\1/g" \
"$ASM/$src.s" > "$ASM/$src.unique.s"
done
cat "$DIR/entry.asm" "$ASM/branch.unique.s" "$ASM/layout.unique.s" \
"$ASM/xhello.unique.s" "$WORK/rom-imports.asm" > "$SDK/M3.ASM"
# The linker command file must keep CRLF line endings.
sed 's/\r$//' "$DIR/link.cmd" | sed 's/$/\r/' > "$SDK/M3.CMD"
rm -f "$SDK/M3.OBJ" "$SDK/M3.OUT" "$SDK/M3.MAP" "$SDK/M3.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 M3 M3.obj M3.lst -l" \
-c "gsplnk M3.cmd" \
-c "exit" -exit > "$WORK/dosbox.log" 2>&1 \
|| { tail -20 "$WORK/dosbox.log" >&2; fail "DOSBox failed"; }
[ -f "$SDK/M3.LST" ] || fail "GSPA produced no listing (see $WORK/dosbox.log)"
sed 's/\r$//' "$SDK/M3.LST" > "$WORK/m3.lst"
grep -q 'No Errors' "$WORK/m3.lst" \
|| { grep -i -B2 -A2 'error' "$WORK/m3.lst" >&2 || true; fail "GSPA reported errors"; }
grep -q 'No Warnings' "$WORK/m3.lst" \
|| { grep -i -B2 -A2 'warning' "$WORK/m3.lst" >&2 || true; fail "GSPA reported warnings"; }
[ -f "$SDK/M3.OUT" ] || fail "GSPLNK produced no M3.OUT"
sed 's/\r$//' "$SDK/M3.MAP" > "$WORK/m3.map"
cp "$SDK/M3.OUT" "$COFF"
cp "$SDK/M3.ASM" "$WORK/m3.asm"
ENTRY=$(awk '$2 == "_start" { print $1; exit }' "$WORK/m3.map")
[ "$ENTRY" = "018A0000" ] || fail "linker entry _start is '$ENTRY', want 018A0000"
grep -q 'ENTRY POINT SYMBOL: "_start"' "$WORK/m3.map" \
|| fail "linker map has no _start entry point"
BSS_ORIGIN=$(awk '$1 == ".bss" && $2 == "0" { print $3; exit }' "$WORK/m3.map")
BSS_LENGTH=$(awk '$1 == ".bss" && $2 == "0" { print $4; exit }' "$WORK/m3.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/m3.map")
DATA_LENGTH=$(awk '$1 == ".data" && $2 == "0" { print $4; exit }' "$WORK/m3.map")
[ -n "$DATA_LENGTH" ] || fail "linker map has no .data section"
[ "$DATA_LENGTH" != "00000000" ] || fail ".data is empty (no \"0::0\" literal)"
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"
# Start from empty NVRAM and pin the monitor: the pixel check assumes the real
# VR319 2048-bit row pitch, and stale NVRAM must not change the boot state.
rm -rf "$WORK/run/nvram"
mkdir -p "$WORK/run/nvram"
VT1200_MONITOR=VR319 \
VT1200_SKIP_LEVEL1=1 \
LOADER_AT="$LOADER_AT" LOADER_END="$LOADER_END" \
LOADER_BIN="$LOADER_BIN" LOADER_COFF="$COFF" \
LOADER_OUT="$WORK/evidence" \
M3_LOADER_LUA="$LOADER_LUA" \
timeout "${MAME_TIMEOUT:-900}" "$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/m3-check.lua" -autoboot_delay 0 \
> "$WORK/mame.log" 2>&1 \
|| { tail -30 "$WORK/mame.log" >&2; fail "MAME run failed"; }
MARKERS=$(grep -E '^M3: display=' "$WORK/mame.log" | tail -1 || true)
[ -n "$MARKERS" ] || { tail -30 "$WORK/mame.log" >&2; fail "MAME produced no M3 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 '^M3 MAME: PASS' "$WORK/mame.log" \
|| { tail -30 "$WORK/mame.log" >&2; fail "M3 marker/window check failed"; }
echo "M3 MAME: PASS"