forked from opencode.energia.arti.ee/vt1200
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.
191 lines
5.8 KiB
Bash
Executable File
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"
|