Files
vt1200/apps/termtris/build.sh
T
opencode ce40582e9d fix(termtris): run the 2ine loader under qemu-i386 when the kernel lacks 16-bit LDT
The r5.01 tools are 16-bit OS/2 NE programs.  A Linux kernel built
without CONFIG_X86_16BIT (this box's cloud kernel) rejects modify_ldt
for 16-bit segments, so lx_loader aborts at its first segment even
though it is a 32-bit binary.  Probe for that failure and use
qemu-i386, whose linux-user modify_ldt supports 16-bit segments.
LX_RUNNER overrides the choice.  The toolchain output is unchanged:
the build completes in ~10 s and the COFF differs only in its header
timestamp.
2026-11-12 05:35:30 +00:00

191 lines
5.8 KiB
Bash
Executable File

#!/bin/sh
# build.sh -- build a VT1200 COFF with TI TMS34010 r5.01 under 2ine.
#
# apps/termtris/build.sh SOURCE.c [SOURCE.c ...] [EXTRA.obj ...]
#
# The first .c is the main program; the output is build/<main>.coff. entry.asm
# and cinit.asm are always assembled and linked. The 2ine loader is outside the
# repo: point NINE at its build directory (default /home/arti/os2-work/2ine/build).
set -eu
DIR=$(cd "$(dirname "$0")" && pwd)
ROOT=$(cd "$DIR/../.." && pwd)
WORK="$DIR/work"
OUT="$DIR/build"
NINE="${NINE:-/home/arti/os2-work/2ine/build}"
LX="$NINE/lx_loader"
SDK="$ROOT/tools/tms34010-sdk/1990-11 TMS340 Family SDK"
Z1="$SDK/1990-11-19 TMS340 Family Code Generation Tools (1 of 2) r5.01 rev2564062-1641.zip"
Z2="$SDK/1990-11-19 TMS340 Family Code Generation Tools (2 of 2) r5.01 rev2564062-1641.zip"
if [ ! -x "$LX" ]; then
echo "build.sh: no 2ine lx_loader at $LX (set NINE=...)" >&2
exit 1
fi
if [ ! -f "$Z1" ] || [ ! -f "$Z2" ]; then
echo "build.sh: r5.01 SDK zips not found under $SDK" >&2
exit 1
fi
mkdir -p "$WORK" "$OUT"
if [ ! -x "$WORK/gspac.exe" ]; then
unzip -oq "$Z1" -d "$WORK"
unzip -oq "$Z2" -d "$WORK"
fi
# The r5.01 tools are 16-bit OS/2 NE programs, which 2ine runs through 16-bit
# LDT segments. A Linux kernel built without CONFIG_X86_16BIT rejects those
# (modify_ldt returns EINVAL), so the loader aborts at its first segment even
# though it is a 32-bit binary. Detect that with a probe run and, when needed,
# drive the same loader through qemu-i386, whose linux-user emulation does
# implement the 16-bit LDT. Set LX_RUNNER to a command prefix (for example
# "qemu-i386") to force one; leave it unset to auto-detect.
if [ -z "${LX_RUNNER:-}" ]; then
if ( cd "$WORK" && "$LX" ./gspac.exe ) 2>&1 |
grep -q 'failed to allocate LDT selector'; then
if command -v qemu-i386 >/dev/null 2>&1; then
LX_RUNNER=qemu-i386
else
echo "build.sh: this kernel has no 16-bit LDT support and qemu-i386 is not installed" >&2
echo "build.sh: install qemu-user, or set LX_RUNNER to a working runner" >&2
exit 1
fi
fi
fi
for so in "$NINE"/lib*.so; do
[ -e "$so" ] && ln -sf "$so" "$WORK/"
done
cp "$DIR/src/vt1200/"*.h "$WORK/" 2>/dev/null || true
cp "$DIR/src/"*.h "$WORK/" 2>/dev/null || true
cp "$DIR/src/vt1200/entry.asm" "$DIR/src/vt1200/cinit.asm" "$DIR/src/vt1200/cinitz.asm" "$WORK/"
# Collect objects. C sources are copied under their uppercase basename because
# the OS/2 tools are case-sensitive on this host.
OBJS=""
MAIN=""
for arg in "$@"; do
case "$arg" in
*.c)
base=$(basename "$arg" .c | tr 'a-z' 'A-Z')
[ -n "$MAIN" ] || MAIN="$base"
cp "$arg" "$WORK/$base.C"
OBJS="$OBJS $base"
;;
*.obj)
cp "$arg" "$WORK/"
OBJS="$OBJS $(basename "$arg")"
;;
*.asm)
base=$(basename "$arg" .asm | tr 'a-z' 'A-Z')
cp "$arg" "$WORK/$base.ASM"
OBJS="$OBJS $base"
;;
*)
echo "build.sh: unsupported input: $arg" >&2
exit 1
;;
esac
done
if [ -z "$MAIN" ]; then
echo "build.sh: give at least one .c source" >&2
exit 1
fi
# The 2ine-hosted tools frequently crash during teardown after doing their work
# (exit 139 / 134). Treat an output file as success, and retry a missing one.
run() {
_out=$1
shift
i=0
while [ "$i" -lt 5 ]; do
( cd "$WORK" && ${LX_RUNNER} "$LX" "$@" ) >/dev/null 2>&1 || true
if [ -f "$WORK/$_out" ]; then
return 0
fi
i=$((i + 1))
done
echo "build.sh: stage failed to produce $WORK/$_out: $*" >&2
return 1
}
compile_c() {
base=$1
rm -f "$WORK/$base.IF" "$WORK/$base.OPT" "$WORK/$base.ASM" "$WORK/$base.obj"
run "$base.IF" ./gspac.exe -k -i. "$base.C" "$base.IF"
run "$base.OPT" ./gspopt.exe -k -O2 "$base.IF" "$base.OPT"
run "$base.ASM" ./gspcg.exe -m "$base.OPT" "$base.ASM" "$base.TMP"
run "$base.obj" ./gspa.exe "$base.ASM"
}
compile_asm() {
base=$1
rm -f "$WORK/$base.obj"
run "$base.obj" ./gspa.exe "$base.ASM"
}
# Compile every collected object exactly once, in the order given.
for arg in "$@"; do
case "$arg" in
*.c) compile_c "$(basename "$arg" .c | tr 'a-z' 'A-Z')" ;;
*.asm) compile_asm "$(basename "$arg" .asm | tr 'a-z' 'A-Z')" ;;
esac
done
compile_asm ENTRY
compile_asm CINIT
compile_asm CINITZ
OUTNAME=${OUTNAME:-$(printf '%s' "$MAIN" | tr 'A-Z' 'a-z')}
CMD="$WORK/$MAIN.CMD"
{
printf '%s\n' '-a'
printf '%s\n' "-o $MAIN.out"
printf '%s\n' "-m $MAIN.map"
printf '%s\n' '-e _start'
printf '%s\n' 'CINIT.obj'
printf '%s\n' 'ENTRY.obj'
for arg in "$@"; do
case "$arg" in
*.c) printf '%s\n' "$(basename "$arg" .c | tr 'a-z' 'A-Z').obj" ;;
*.asm) printf '%s\n' "$(basename "$arg" .asm | tr 'a-z' 'A-Z').obj" ;;
*.obj) printf '%s\n' "$(basename "$arg")" ;;
esac
done
printf '%s\n' '-l rts.lib'
printf '%s\n' 'MEMORY'
printf '%s\n' '{'
printf '%s\n' ' prog: origin = 018A0000h, length = 20000h'
printf '%s\n' ' ram: origin = 01A00000h, length = 40000h'
printf '%s\n' '}'
printf '%s\n' 'SECTIONS'
printf '%s\n' '{'
printf '%s\n' ' .text: {} > prog'
printf '%s\n' ' .data: {} > prog'
printf '%s\n' ' .cinit: {} > prog'
printf '%s\n' ' .bss: {} > ram'
printf '%s\n' '}'
} > "$CMD"
sed -i 's/$/\r/' "$CMD"
rm -f "$WORK/$MAIN.out" "$WORK/$MAIN.map"
run "$MAIN.out" ./gsplnk.exe "$MAIN.CMD"
if [ ! -f "$WORK/$MAIN.map" ]; then
echo "build.sh: no link map" >&2
exit 1
fi
# An empty .cinit makes the linker's `cinit` symbol point at .text; the entry's
# cinit walker would then treat an instruction as a record count. Relink with
# a one-word terminator when the map says the section is empty.
CSIZE=$(awk '$1 == ".cinit" {print $4; exit}' "$WORK/$MAIN.map")
if [ "$CSIZE" = "00000000" ]; then
sed -i 's/CINIT\.obj/CINITZ.obj/' "$CMD"
rm -f "$WORK/$MAIN.out" "$WORK/$MAIN.map"
run "$MAIN.out" ./gsplnk.exe "$MAIN.CMD"
fi
cp "$WORK/$MAIN.out" "$OUT/$OUTNAME.coff"
sed 's/\r$//' "$WORK/$MAIN.map" > "$OUT/$OUTNAME.map"
echo "build.sh: $OUT/$OUTNAME.coff ($(wc -c < "$OUT/$OUTNAME.coff") bytes)"
sed -n '1,40p' "$OUT/$OUTNAME.map"