#!/bin/sh # M1 end-to-end smoke test. # # scripts/m1/run.sh # # Compiles six C functions 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: # # add(20, 22) == 42 # load(&word) == 0x12345678 # second() == &arr[1] (arr + 32 bits) # loadb(&byte) == 0xFFFFFF81 (sign-extended 0x81) # storeb(&byte, 0x81) leaves the byte 0x81 # # Overridable: TMS_CLANG TMS_OPT TMS_BITADDR TMS_LLC MAME DOSBOX DOSBOX_CONF # SDK_IMG M1_WORK M1_COFF M1_SDK LOADER_AT LOADER_END set -eu ROOT=$(cd "$(dirname "$0")/../.." && pwd) DIR=$ROOT/scripts/m1 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=${M1_WORK:-$ROOT/build/m1} COFF=${M1_COFF:-$WORK/m1.coff} SDK=${M1_SDK:-$ROOT/build/m1-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} fail() { echo "m1: $*" >&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 add load late const_gep char; 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 echo "== check the bit-address constants" grep -Eq '^[[:space:]]*ADDI[[:space:]]+24,[[:space:]]*A0' "$ASM/late.s" \ || fail "late.s has no ADDI 24 (late GEP got scaled again?)" grep -Eq 'ADDI[[:space:]]+192' "$ASM/late.s" \ && fail "late.s contains ADDI 192: late GEP was scaled twice" grep -Eq '^[[:space:]]*MOVI[[:space:]]+_arr\+32,[[:space:]]*A0' "$ASM/const_gep.s" \ || fail "const_gep.s has no 'MOVI _arr+32': constant GEP addend is not in bits" grep -Eq '_arr\+4[^0-9]' "$ASM/const_gep.s" \ && fail "const_gep.s references _arr+4 (bytes) instead of _arr+32 (bits)" echo " late.s: $(grep -E 'ADDI' "$ASM/late.s" | head -1 | sed 's/^[[:space:]]*//')" echo " const_gep.s: $(grep -E 'MOVI' "$ASM/const_gep.s" | head -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/add.s" "$ASM/load.s" "$ASM/late.s" \ "$ASM/const_gep.s" "$ASM/char.s" "$DIR/char-test.asm" > "$SDK/M1.ASM" # The linker command file must keep CRLF line endings. sed 's/\r$//' "$DIR/link.cmd" | sed 's/$/\r/' > "$SDK/M1.CMD" rm -f "$SDK/M1.OBJ" "$SDK/M1.OUT" "$SDK/M1.MAP" "$SDK/M1.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 M1 M1.obj M1.lst -l" \ -c "gsplnk M1.cmd" \ -c "exit" -exit > "$WORK/dosbox.log" 2>&1 \ || { tail -20 "$WORK/dosbox.log" >&2; fail "DOSBox failed"; } [ -f "$SDK/M1.LST" ] || fail "GSPA produced no listing (see $WORK/dosbox.log)" sed 's/\r$//' "$SDK/M1.LST" > "$WORK/m1.lst" grep -q 'No Errors' "$WORK/m1.lst" \ || { grep -i -B2 -A2 'error' "$WORK/m1.lst" >&2 || true; fail "GSPA reported errors"; } grep -q 'No Warnings' "$WORK/m1.lst" \ || { grep -i -B2 -A2 'warning' "$WORK/m1.lst" >&2 || true; fail "GSPA reported warnings"; } [ -f "$SDK/M1.OUT" ] || fail "GSPLNK produced no M1.OUT" sed 's/\r$//' "$SDK/M1.MAP" > "$WORK/m1.map" cp "$SDK/M1.OUT" "$COFF" cp "$SDK/M1.ASM" "$WORK/m1.asm" ENTRY=$(awk '$2 == "_start" { print $1; exit }' "$WORK/m1.map") [ "$ENTRY" = "018A0000" ] || fail "linker entry _start is '$ENTRY', want 018A0000" grep -q 'ENTRY POINT SYMBOL: "_start"' "$WORK/m1.map" \ || fail "linker map has no _start entry point" BSS_ORIGIN=$(awk '$1 == ".bss" && $2 == "0" { print $3; exit }' "$WORK/m1.map") BSS_LENGTH=$(awk '$1 == ".bss" && $2 == "0" { print $4; exit }' "$WORK/m1.map") [ -n "$BSS_ORIGIN" ] || fail "linker map has no .bss section" [ "$BSS_LENGTH" = "00000080" ] \ || fail ".bss is $BSS_LENGTH bits, expected 00000080 (only arr[4])" # Pin the required layout independently of the map: &arr[1] is arr + 32 bits # and arr is the first object in the .bss section that link.cmd puts at ram. [ "$BSS_ORIGIN" = "018A9000" ] \ || fail "linker .bss origin is $BSS_ORIGIN, want 018A9000 (link.cmd ram origin)" EXPECT_SECOND=0x018A9020 DERIVED_SECOND=$(printf '0x%08X' "$((0x$BSS_ORIGIN + 32))") [ "$DERIVED_SECOND" = "$EXPECT_SECOND" ] \ || fail ".bss origin gives &arr[1] = $DERIVED_SECOND, want $EXPECT_SECOND" echo " linked .text at 018A0000, .bss at $BSS_ORIGIN (128 bits)" echo " expected &arr[1] = $EXPECT_SECOND" 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" \ M1_LOADER_LUA="$LOADER_LUA" \ M1_EXPECT_LOAD=0x12345678 M1_EXPECT_SECOND="$EXPECT_SECOND" \ 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/m1-check.lua" -autoboot_delay 0 \ > "$WORK/mame.log" 2>&1 \ || { tail -30 "$WORK/mame.log" >&2; fail "MAME run failed"; } MARKERS=$(grep -E '^M1: add=' "$WORK/mame.log" | tail -1 || true) [ -n "$MARKERS" ] || { tail -30 "$WORK/mame.log" >&2; fail "MAME produced no M1 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 '^M1 MAME: PASS' "$WORK/mame.log" \ || { tail -30 "$WORK/mame.log" >&2; fail "marker check failed"; } echo "M1 MAME: PASS"