#!/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 # demos/.c -> demos/.omf (compiled with clang) # demos/.cpp -> demos/.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), plus libunwindStub.o # (Itanium `_Unwind_*` surface routed onto SJLJ — Phase 5.1). 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)" # --debug parallels demos/build.sh: emit `-g` IR, ask link816 for a # DWARF sidecar, suffix outputs with `_dbg` so debug + release coexist. # Phase 3.1 (debugger front-end). DEBUG=0 ARGS=() while [ $# -gt 0 ]; do case "$1" in --debug) DEBUG=1; shift;; *) ARGS+=("$1"); shift;; esac done set -- "${ARGS[@]+"${ARGS[@]}"}" [ $# -ge 1 ] || { echo "usage: $0 [--debug] " >&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++" # Default is -fno-exceptions -fno-rtti (the supported subset for # third-party C++). Probes that exercise the SJLJ exception runtime # (e.g. unwindStubProbe.cpp) opt in via GNO_CXX_EXCEPTIONS=1, which # switches to -fsjlj-exceptions and pulls in the libunwindStub.o # symbols. Set GNO_CXX_RTTI=1 alongside if you need typeinfo # objects (not currently exercised by any in-tree probe). if [ "${GNO_CXX_EXCEPTIONS:-0}" = 1 ]; then LANG_FLAGS="-fsjlj-exceptions" if [ "${GNO_CXX_RTTI:-0}" != 1 ]; then LANG_FLAGS="$LANG_FLAGS -fno-rtti" fi else LANG_FLAGS="-fno-exceptions -fno-rtti" fi 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" if [ "$DEBUG" = 1 ]; then OUTBASE="${BASE}_dbg" DBGFLAGS="-g" else OUTBASE="$BASE" DBGFLAGS="" fi OBJ="$SCRIPT_DIR/$OUTBASE.o" BIN="$SCRIPT_DIR/$OUTBASE.bin" MAP="$SCRIPT_DIR/$OUTBASE.map" RELOC="$SCRIPT_DIR/$OUTBASE.reloc" OUT="$SCRIPT_DIR/$OUTBASE.omf" DWARF="$SCRIPT_DIR/$OUTBASE.dwarf" echo "compile: $(basename "$SRC") -> $OUTBASE.o" # `-I include/c++` makes 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++" $DBGFLAGS -O2 -ffunction-sections $LANG_FLAGS ${GNO_CFLAGS:-} -c "$SRC" -o "$OBJ" echo "link: -> $OUTBASE.bin" LINKER_ARGS=(-o "$BIN" --text-base 0x1000 --bss-base 0xA000 \ --map "$MAP" --reloc-out "$RELOC") if [ "$DEBUG" = 1 ]; then LINKER_ARGS+=(--debug-out "$DWARF") fi "$LINK" "${LINKER_ARGS[@]}" \ "$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/math.o" \ "$RT/iigsToolbox.o" \ "$RT/libgcc.o" \ "$RT/libcxxabi.o" "$RT/libcxxabiSjlj.o" "$RT/libunwindStub.o" echo "OMF: -> $OUTBASE.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 "$OUTBASE" | tr '[:lower:]' '[:upper:]' | cut -c1-8)" \ --expressload --relocs "$RELOC" --stack-size "${GNO_STACK_SIZE:-0x4000}" ls -la "$OUT" if [ "$DEBUG" = 1 ]; then echo "debug sidecar: $DWARF" echo "map: $MAP" fi echo "done: $OUT (run with: bash scripts/runInGno.sh $OUT --check 0x025000=C0DE)"