65816-llvm-mos/demos/buildGno.sh
2026-05-30 19:40:29 -05:00

79 lines
3.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# buildGno.sh — compile a C or C++ source into a GNO/ME-loadable OMF
# shell command.
#
# Usage: bash demos/buildGno.sh <basename>
# demos/<basename>.c -> demos/<basename>.omf (compiled with clang)
# demos/<basename>.cpp -> demos/<basename>.omf (compiled with clang++)
#
# Uses crt0Gno (GNO entry contract) + the GNO libc backend, then wraps
# the flat image in a relocatable ExpressLoad OMF via omfEmit — the
# GS/OS Loader (which GNO uses to launch commands) requires a real OMF,
# not a flat binary. C++ programs additionally link libcxxabi.o
# (operator new/delete/__cxa_atexit/__cxa_guard_*/dynamic_cast/typeinfo)
# and libcxxabiSjlj.o (SJLJ exception runtime). Link-time GC removes
# whichever portions aren't referenced, so the cost is zero for pure-C
# programs.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
[ $# -ge 1 ] || { echo "usage: $0 <basename>" >&2; exit 2; }
BASE="$1"
if [ -f "$SCRIPT_DIR/$BASE.cpp" ]; then
SRC="$SCRIPT_DIR/$BASE.cpp"
CC="$ROOT/tools/llvm-mos-build/bin/clang++"
LANG_FLAGS="-fno-exceptions -fno-rtti"
elif [ -f "$SCRIPT_DIR/$BASE.c" ]; then
SRC="$SCRIPT_DIR/$BASE.c"
CC="$ROOT/tools/llvm-mos-build/bin/clang"
LANG_FLAGS=""
else
echo "no source: $SCRIPT_DIR/$BASE.{c,cpp}" >&2
exit 2
fi
LINK="$ROOT/tools/link816"
OMF="$ROOT/tools/omfEmit"
RT="$ROOT/runtime"
OBJ="$SCRIPT_DIR/$BASE.o"
BIN="$SCRIPT_DIR/$BASE.bin"
MAP="$SCRIPT_DIR/$BASE.map"
RELOC="$SCRIPT_DIR/$BASE.reloc"
OUT="$SCRIPT_DIR/$BASE.omf"
echo "compile: $(basename "$SRC") -> $BASE.o"
# `-I include/c++` makes <etl/...> headers findable when the source is .cpp.
# Harmless for .c — the directory just doesn't contain anything reachable
# from a C TU.
"$CC" --target=w65816 -I"$RT/include" -I"$RT/include/c++" -O2 -ffunction-sections $LANG_FLAGS -c "$SRC" -o "$OBJ"
echo "link: -> $BASE.bin"
"$LINK" -o "$BIN" --text-base 0x1000 --bss-base 0xA000 \
--map "$MAP" --reloc-out "$RELOC" \
"$RT/crt0Gno.o" "$OBJ" \
"$RT/libcGno.o" "$RT/gnoKernel.o" "$RT/gnoGsos.o" \
"$RT/libc.o" "$RT/snprintf.o" "$RT/extras.o" \
"$RT/softFloat.o" "$RT/softDouble.o" \
"$RT/libgcc.o" \
"$RT/libcxxabi.o" "$RT/libcxxabiSjlj.o"
echo "OMF: -> $BASE.omf"
# Declare a dedicated DP/Stack (OMF KIND=0x1012) segment. Without it GNO
# falls back to a 4 KB default stack shared with DP and placed low in bank 0,
# which is too small / mis-placed for GS/OS file I/O: GNO's GS/OS interceptor
# (StackGSOS) carves its direct-page work area off the caller's S and the FST
# nests deep below it, corrupting the saved SP on certain stack-buffer layouts.
# A sized $12 segment makes the Loader allocate a larger bank-0 block (S set
# high in it), giving headroom and moving stack buffers off the collision.
# This is the idiomatic ORCA/GNO mechanism (== #pragma stacksize).
"$OMF" --input "$BIN" --map "$MAP" \
--base 0x1000 --entry __start --output "$OUT" \
--name "$(echo "$BASE" | tr '[:lower:]' '[:upper:]' | cut -c1-8)" \
--expressload --relocs "$RELOC" --stack-size "${GNO_STACK_SIZE:-0x4000}"
ls -la "$OUT"
echo "done: $OUT (run with: bash scripts/runInGno.sh $OUT --check 0x025000=C0DE)"