tests: link M1 char helpers and check MOVB

This commit is contained in:
arti committed 2026-11-11 08:53:41 +00:00
1 parent b638ab0fad
commit 755b3cb7b2
7 files changed
+98 -26

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
; char-test.asm -- run the compiled char helpers and publish their results.
;
; Appended after char.s so _loadb/_storeb (and every earlier function) keep
; their linked addresses. _start calls this instead of calling _second
; directly, which is the same one-word-size calla; _char_test calls _second
; and returns its result in A0 so the existing second() marker still works.
;
; storeb(018A8080, 0x81) writes the byte 0x81
; loadb(018A8080) returns the sign-extended 0xFFFFFF81
.text
.globl _char_test
_char_test:
calla _second ; A0 = &arr[1]
move a0, a3 ; save it for the second() marker
movi 018A8080h, a0 ; p = storeb/loadb marker byte
movi 81h, a1 ; v = 0x81 (low byte stored by MOVB)
calla _storeb
calla _loadb ; A0 is still 018A8080
movi 018A8060h, a2
move a0, *a2, 1 ; 32-bit loadb marker
move a3, a0 ; restore &arr[1]
rets
+2
View File
@@ -0,0 +1,2 @@
int loadb(char *p) { return *p; }
void storeb(char *p, int v) { *p = v; }
+35 -13
View File
@@ -12,6 +12,8 @@
# _load MOVE *A0, A0, 1 ; RETS 0
# _late ADDI 24, A0 ; RETS 0
# _second MOVI _arr+32, A0 ; RETS 0
# _loadb MOVB *A0, A0 ; RETS 0
# _storeb MOVB A1, *A0 ; RETS 0
#
# This verifies the 1987 GSPA assembler's encoding, not a dormant LLVM Inst
# encoding. Overridable: M1_COFF DIS34
@@ -121,31 +123,49 @@ ADD=$(get_sym _add)
LOAD=$(get_sym _load)
LATE=$(get_sym _late)
SECOND=$(get_sym _second)
LOADB=$(get_sym _loadb)
STOREB=$(get_sym _storeb)
ADD_HEX=$(printf '%08X' "$ADD")
LOAD_HEX=$(printf '%08X' "$LOAD")
LATE_HEX=$(printf '%08X' "$LATE")
SECOND_HEX=$(printf '%08X' "$SECOND")
LOADB_HEX=$(printf '%08X' "$LOADB")
STOREB_HEX=$(printf '%08X' "$STOREB")
[ "$ADD_HEX" = "018A0240" ] || fail "_add linked at $ADD_HEX, want 018A0240"
[ "$LOAD_HEX" = "018A0270" ] || fail "_load linked at $LOAD_HEX, want 018A0270"
[ "$LATE_HEX" = "018A0290" ] || fail "_late linked at $LATE_HEX, want 018A0290"
[ "$SECOND_HEX" = "018A02C0" ] || fail "_second linked at $SECOND_HEX, want 018A02C0"
[ "$LOADB_HEX" = "018A0300" ] || fail "_loadb linked at $LOADB_HEX, want 018A0300"
[ "$STOREB_HEX" = "018A0320" ] || fail "_storeb linked at $STOREB_HEX, want 018A0320"
echo " _add=$ADD_HEX _load=$LOAD_HEX _late=$LATE_HEX _second=$SECOND_HEX"
echo " _loadb=$LOADB_HEX _storeb=$STOREB_HEX (appended after _second)"
echo "== disassemble the emitted .text with dis34"
"$DIS34" -b "$TEXT_HEX" -l -o "$TMP/text.bin" > "$TMP/dis34.txt" 2> "$TMP/dis34.err" \
|| { cat "$TMP/dis34.err" >&2; fail "dis34 failed"; }
[ -s "$TMP/dis34.txt" ] || fail "dis34 produced no output"
# match_at FILE DECIMAL_ADDR ERE
# dis34 -l prints "018a0240:\tadd a0, a1\t; 4001". The operand must be
# followed by optional whitespace and then the comment/end-of-line marker, so
# "rets 1" cannot satisfy a "rets" pattern.
match_at() {
_file=$1
_addr=$2
_pat=$3
_laddr=$(printf '%08x' "$_addr")
grep -Eiq "^${_laddr}:[[:space:]]+${_pat}([[:space:]]*(;|$))" "$_file"
}
# expect_at LABEL DECIMAL_ADDR EXPECTED_TEXT ERE
# dis34 -l prints "018a0240:\tadd a0, a1\t; 4001".
expect_at() {
_label=$1
_addr=$2
_want=$3
_pat=$4
_laddr=$(printf '%08x' "$_addr")
if grep -Eiq "^${_laddr}:[[:space:]]+${_pat}([[:space:]]|;|$)" "$TMP/dis34.txt"; then
if match_at "$TMP/dis34.txt" "$_addr" "$_pat"; then
echo " ok $_label @ 0x$_laddr: $_want"
return 0
fi
@@ -191,16 +211,18 @@ expect_label "$SECOND"
expect_at "_second" "$SECOND" "MOVI _arr+32, A0" 'movi[[:space:]]+(0x)?18a9020,[[:space:]]*a0'
expect_at "_second+48" $((SECOND + 48)) "RETS 0" 'rets([[:space:]]+0)?'
# The char helpers are lit-only in M1, so the harness does not link them.
# If a later harness does, do not silently skip their encoding.
MOVB_SEEN=0
for name in _loadb _storeb; do
if addr=$(sym_addr "$name"); then
MOVB_SEEN=1
expect_at "$name" "$addr" "MOVB" 'movb[[:space:]]+'
fi
done
[ "$MOVB_SEEN" -eq 1 ] \
|| echo " movb: no _loadb/_storeb linked (char helpers are lit-only)"
# Negative control for the RETS operand boundary: "rets 1" must not match.
printf '018a0260:\trets\t1\t; 0960\n' > "$TMP/rets-neg.txt"
if match_at "$TMP/rets-neg.txt" $((ADD + 32)) 'rets([[:space:]]+0)?'; then
fail "negative control: the RETS pattern accepted 'rets 1'"
fi
echo " ok negative control @ 0x$(printf '%08x' $((ADD + 32))): 'rets 1' rejected"
expect_label "$LOADB"
expect_at "_loadb" "$LOADB" "MOVB *A0, A0" 'movb[[:space:]]+\*a0,[[:space:]]*a0'
expect_at "_loadb+16" $((LOADB + 16)) "RETS 0" 'rets([[:space:]]+0)?'
expect_at "_storeb" "$STOREB" "MOVB A1, *A0" 'movb[[:space:]]+a1,[[:space:]]*\*a0'
expect_at "_storeb+16" $((STOREB + 16)) "RETS 0" 'rets([[:space:]]+0)?'
echo "M1 dis34 round-trip: PASS"
+9 -1
View File
@@ -7,9 +7,16 @@
; 018A8000 add(20, 22)
; 018A8020 load(018A8100)
; 018A8040 second() == &arr[1]
; 018A8060 loadb(018A8080) == 0xFFFFFF81 (sign-extended 0x81)
; 018A8080 storeb(018A8080, 0x81) leaves the byte 0x81
;
; _late is linked but not called: it is a compile-time check that a surviving
; GEP offset is 24 bits, not 192.
;
; The char helpers are exercised by _char_test (char-test.asm, appended after
; the compiled functions). Calling it here instead of calling _second keeps
; this entry's size -- and therefore every function's linked address -- the
; same; _char_test calls _second and returns its result in A0.
.text
.globl _start
_start:
@@ -29,7 +36,8 @@ _start:
movi 018A8020h, a2
move a0, *a2, 1
calla _second
calla _char_test
movi 018A8040h, a2
move a0, *a2, 1
+13 -5
View File
@@ -4,6 +4,8 @@
-- M1_LOADER_LUA path to vendor/vt1200/dwt/loader/loader.lua
-- M1_EXPECT_LOAD expected load() result (default 0x12345678)
-- M1_EXPECT_SECOND expected &arr[1] in bits
-- M1_EXPECT_LOADB expected sign-extended byte load (default 0xFFFFFF81)
-- M1_EXPECT_STOREB expected byte store (default 0x81)
--
-- The marker read runs one frame before loader.lua's inspection/exit frame.
@@ -12,6 +14,8 @@ dofile(os.getenv("M1_LOADER_LUA"))
local ENDF = tonumber(os.getenv("LOADER_END") or "2700")
local EXPECT_LOAD = tonumber(os.getenv("M1_EXPECT_LOAD") or "0x12345678")
local EXPECT_SECOND = tonumber(os.getenv("M1_EXPECT_SECOND") or "0x018A9020")
local EXPECT_LOADB = tonumber(os.getenv("M1_EXPECT_LOADB") or "0xFFFFFF81")
local EXPECT_STOREB = tonumber(os.getenv("M1_EXPECT_STOREB") or "0x81")
local frames = 0
local checked = false
@@ -23,14 +27,18 @@ emu.register_frame_done(function()
local add = prog:read_u32(0x018A8000)
local load = prog:read_u32(0x018A8020)
local second = prog:read_u32(0x018A8040)
print(string.format("M1: add=%d load=0x%08X second=0x%08X",
add, load, second))
if add == 42 and load == EXPECT_LOAD and second == EXPECT_SECOND then
local loadb = prog:read_u32(0x018A8060)
local storeb = prog:read_u8(0x018A8080)
print(string.format(
"M1: add=%d load=0x%08X second=0x%08X loadb=0x%08X storeb=0x%02X",
add, load, second, loadb, storeb))
if add == 42 and load == EXPECT_LOAD and second == EXPECT_SECOND
and loadb == EXPECT_LOADB and storeb == EXPECT_STOREB then
print("M1 MAME: PASS")
else
print(string.format(
"M1 MAME: FAIL (want add=42 load=0x%08X second=0x%08X)",
EXPECT_LOAD, EXPECT_SECOND))
"M1 MAME: FAIL (want add=42 load=0x%08X second=0x%08X loadb=0x%08X storeb=0x%02X)",
EXPECT_LOAD, EXPECT_SECOND, EXPECT_LOADB, EXPECT_STOREB))
end
end
end, "m1-check")
+10 -6
View File
@@ -3,16 +3,18 @@
#
# scripts/m1/run.sh
#
# Compiles four C functions with scripts/tms-clang + llc -O0, assembles and
# 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_SDK LOADER_AT LOADER_END
# SDK_IMG M1_WORK M1_COFF M1_SDK LOADER_AT LOADER_END
set -eu
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
@@ -30,6 +32,7 @@ DOSBOX_CONF=${DOSBOX_CONF:-$ROOT/vendor/vt1200/harness/sdk-scratch/dosbox-bench.
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
@@ -47,10 +50,11 @@ for tool in "$TMS_CLANG" "$TMS_OPT" "$TMS_BITADDR" "$TMS_LLC"; do
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; do
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" \
@@ -82,7 +86,7 @@ fi
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" > "$SDK/M1.ASM"
"$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"
@@ -102,7 +106,7 @@ grep -q 'No Errors' "$WORK/m1.lst" \
|| { grep -i -B2 -A2 'error' "$WORK/m1.lst" >&2 || true; fail "GSPA reported errors"; }
[ -f "$SDK/M1.OUT" ] || fail "GSPLNK produced no M1.OUT"
sed 's/\r$//' "$SDK/M1.MAP" > "$WORK/m1.map"
cp "$SDK/M1.OUT" "$WORK/m1.coff"
cp "$SDK/M1.OUT" "$COFF"
cp "$SDK/M1.ASM" "$WORK/m1.asm"
ENTRY=$(awk '$2 == "_start" { print $1; exit }' "$WORK/m1.map")
@@ -133,7 +137,7 @@ echo "== run the COFF in MAME"
VT1200_SKIP_LEVEL1=1 \
LOADER_AT="$LOADER_AT" LOADER_END="$LOADER_END" \
LOADER_BIN="$LOADER_BIN" LOADER_COFF="$WORK/m1.coff" \
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" \
+7 -1
View File
@@ -5,6 +5,12 @@ set -eu
ROOT=$(cd "$(dirname "$0")/../../.." && pwd)
# Always rebuild: a stale build/m1/m1.coff would let an asm-encoding
# regression pass without ever touching the compiler or assembler.
# regression pass without ever touching the compiler or assembler. Keep
# dis-roundtrip.sh pointed at the file this run produces, including when
# M1_WORK/M1_COFF are overridden.
WORK=${M1_WORK:-$ROOT/build/m1}
M1_COFF=${M1_COFF:-$WORK/m1.coff}
export M1_COFF
"$ROOT/scripts/m1/run.sh" "$@"
exec "$ROOT/scripts/m1/dis-roundtrip.sh" "$@"