127 lines
4.7 KiB
Bash
Executable file
127 lines
4.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# build.sh - compile a demo C source into a GS/OS-loadable OMF.
|
||
#
|
||
# Usage: bash demos/build.sh <basename>
|
||
# where demos/<basename>.c is the source. Output is
|
||
# demos/<basename>.omf in the same directory.
|
||
#
|
||
# Uses crt0Gsos (the GS/OS-aware crt) and ExpressLoad-wrapped multi-
|
||
# seg OMF so the slow-Loader rejection path is avoided. The OMF
|
||
# launches via runViaFinder.sh after cadius-injection onto the GS/OS
|
||
# data disk.
|
||
|
||
set -euo pipefail
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
|
||
# --debug appends `_dbg` to the output basename so the release and debug
|
||
# artifacts can coexist on disk. It adds `-g` to clang, requests a DWARF
|
||
# sidecar (`--debug-out`) from link816, and keeps the .map (always emitted
|
||
# regardless of mode — pc2line.py and scripts/mameDebug.py need it for
|
||
# function-name lookup). 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] <basename>" >&2; exit 2; }
|
||
BASE="$1"
|
||
SRC="$SCRIPT_DIR/$BASE.c"
|
||
[ -f "$SRC" ] || { echo "no source: $SRC" >&2; exit 2; }
|
||
|
||
CLANG="$PROJECT_ROOT/tools/llvm-mos-build/bin/clang"
|
||
LINK="$PROJECT_ROOT/tools/link816"
|
||
OMF="$PROJECT_ROOT/tools/omfEmit"
|
||
|
||
# Debug builds get a `_dbg` suffix so they coexist with the release
|
||
# build. All intermediate + final paths share the suffix so a stale
|
||
# release .o isn't reused for a debug link.
|
||
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: $BASE.c -> $OUTBASE.o"
|
||
"$CLANG" --target=w65816 -I"$PROJECT_ROOT/runtime/include" \
|
||
$DBGFLAGS \
|
||
-O2 -ffunction-sections -c "$SRC" -o "$OBJ"
|
||
|
||
echo "link: -> $OUTBASE.bin"
|
||
# text-base 0: under the GS/OS Loader our segment lands at some bank
|
||
# the Loader picks (KIND=0x1000), so link-time addresses can start at
|
||
# offset 0 in the segment. With text-base=0 the link-time-baked IMM16
|
||
# operand for `lda gFoo` already equals gFoo's runtime offset within
|
||
# the segment (no segPlacedBase adjustment needed since the Loader
|
||
# places at bank-aligned bases), so the linker can skip emitting
|
||
# cRELOC entries for every IMM16 site — 60–90% reduction in cRELOC
|
||
# count, proportional speed-up in the Loader's per-reloc walk.
|
||
# Bare-metal builds keep text-base=0x1000 (runInMame.sh loads at
|
||
# bank-0:1000 and link-time addresses must match).
|
||
#
|
||
# bss-base 0xA000 keeps BSS above the SHR shadow region ($2000-$9FFF
|
||
# in bank 0 mirrors to bank E1 SHR memory). Without this, the smaller
|
||
# section-gc'd demos place BSS at e.g. $2300 and global writes scribble
|
||
# on the screen.
|
||
LINKER_ARGS=(-o "$BIN" --text-base 0 --bss-base 0xA000 \
|
||
--map "$MAP" --reloc-out "$RELOC")
|
||
if [ "$DEBUG" = 1 ]; then
|
||
LINKER_ARGS+=(--debug-out "$DWARF")
|
||
fi
|
||
"$LINK" "${LINKER_ARGS[@]}" \
|
||
"$PROJECT_ROOT/runtime/crt0Gsos.o" "$OBJ" \
|
||
"$PROJECT_ROOT/runtime/libc.o" \
|
||
"$PROJECT_ROOT/runtime/snprintf.o" \
|
||
"$PROJECT_ROOT/runtime/extras.o" \
|
||
"$PROJECT_ROOT/runtime/softFloat.o" \
|
||
"$PROJECT_ROOT/runtime/softDouble.o" \
|
||
"$PROJECT_ROOT/runtime/iigsGsos.o" \
|
||
"$PROJECT_ROOT/runtime/iigsToolbox.o" \
|
||
"$PROJECT_ROOT/runtime/iigsToolboxExtra.o" \
|
||
"$PROJECT_ROOT/runtime/desktop.o" \
|
||
"$PROJECT_ROOT/runtime/sound.o" \
|
||
"$PROJECT_ROOT/runtime/cursor.o" \
|
||
"$PROJECT_ROOT/runtime/eventLoop.o" \
|
||
"$PROJECT_ROOT/runtime/uiBuilder.o" \
|
||
"$PROJECT_ROOT/runtime/resource.o" \
|
||
"$PROJECT_ROOT/runtime/libgcc.o"
|
||
|
||
echo "OMF: -> $OUTBASE.omf"
|
||
"$OMF" --input "$BIN" --map "$MAP" \
|
||
--base 0 --entry __start --output "$OUT" \
|
||
--name "$(echo "$OUTBASE" | tr '[:lower:]' '[:upper:]' | cut -c1-8)" \
|
||
--expressload --relocs "$RELOC"
|
||
|
||
ls -la "$OUT"
|
||
if [ "$DEBUG" = 1 ]; then
|
||
echo "debug sidecar: $DWARF"
|
||
echo "map: $MAP"
|
||
fi
|
||
|
||
# Phase 3.4 (rsrcBundle): if demos/<basename>.rsrc/ exists, run the
|
||
# bundler to produce an AppleSingle blob (out.apl) AND the cadius
|
||
# sidecar (out.apl_ResourceFork.bin). We pass --sidecar because
|
||
# cadius v1.4.6's AppleSingle parser drops resource_fork entries
|
||
# silently; runViaFinder.sh uses ADDFILE which picks up the sidecar.
|
||
RSRC_DIR="$SCRIPT_DIR/$BASE.rsrc"
|
||
if [ -d "$RSRC_DIR" ]; then
|
||
APL="$SCRIPT_DIR/$OUTBASE.apl"
|
||
echo "rsrcBundle: $RSRC_DIR -> $OUTBASE.apl (+ sidecar)"
|
||
python3 "$PROJECT_ROOT/tools/rsrcBundle/rsrcBundle.py" \
|
||
--data "$OUT" --rsrc-dir "$RSRC_DIR" --out "$APL" --sidecar
|
||
fi
|
||
|
||
echo "done: $OUT"
|