Add -p (play) and -P (play cycle-exact) flags to build.sh

Previously the only VICE mode was the foreground -v / -V which
exited immediately because the autostart mechanism doesn't
recognize raw .prg files. This change:

- Adds -p / -P: compile, wrap the .prg in a .d64 (using c1541),
  then launch x64 / x64sc detached with setsid+nohup so the
  emulator survives the build.sh exit. Prints the VICE PID and
  the default joystick key bindings (WASD+LCtrl for port 2
  Scoot, Arrows+RShift for port 1 Hare).

- Adds --kill to terminate the detached VICE process.

- Documents the default joystick bindings and a 'press fire on
  both ports' hint to start the game.

Tested: the VICE window appears in the X client list (xlsclients)
on the GNOME session, the .d64 autostarts whack_hare.prg, and
--kill cleanly stops the process.
This commit is contained in:
ballz
2026-07-17 08:30:56 +02:00
parent b7bd76dee3
commit aa9499766f
+90 -39
View File
@@ -4,8 +4,11 @@
# Usage: # Usage:
# ./build.sh # compile main.c → build/whack_hare.prg (-O1) # ./build.sh # compile main.c → build/whack_hare.prg (-O1)
# ./build.sh -e # run in oscar64's built-in emulator (headless, fast) # ./build.sh -e # run in oscar64's built-in emulator (headless, fast)
# ./build.sh -v # run in VICE x64 (interactive, needs a display) # ./build.sh -p # PLAY: compile, wrap in .d64, launch VICE x64 detached
# ./build.sh -V # run in VICE x64sc (interactive, cycle-exact, slow) # ./build.sh -P # PLAY: same as -p but with VICE x64sc (cycle-exact)
# ./build.sh -v # run in VICE x64 (foreground, blocks terminal)
# ./build.sh -V # run in VICE x64sc (foreground, blocks terminal)
# ./build.sh --kill # kill any running VICE x64/x64sc process
# ./build.sh -c # just compile # ./build.sh -c # just compile
# ./build.sh -O0 # compile with -O0 (no optimization; debug build) # ./build.sh -O0 # compile with -O0 (no optimization; debug build)
# ./build.sh -O2 # compile with -O2 (more aggressive inlining) # ./build.sh -O2 # compile with -O2 (more aggressive inlining)
@@ -18,41 +21,53 @@
# whack_hare.asm — full 6502 listing # whack_hare.asm — full 6502 listing
# whack_hare.map — region/section/object placement # whack_hare.map — region/section/object placement
# whack_hare.lbl — VICE monitor label commands # whack_hare.lbl — VICE monitor label commands
# whack.d64 — disk image wrapping whack_hare.prg (for VICE autostart)
# #
# For the development loop, use -e (oscar64's built-in emulator). It runs # For the development loop, use -e (oscar64's built-in emulator). It runs
# without a display, needs no ROMs, and is faster than VICE. VICE is for # without a display, needs no ROMs, and is faster than VICE. Use -p when
# interactive use in a real terminal session, or for cycle-exact validation # you want to play the game interactively — it launches VICE in the
# of raster IRQ and SID timing — which requires a working X display. # background and returns control to your shell so you can do other things.
#
# Default joystick keys in VICE (Settings → Input devices → Joystick
# settings to rebind if needed):
# Port 2 (left, Scoot): W/A/S/D for up/left/right/down, Left Ctrl for fire
# Port 1 (right, Hare): Arrow keys for up/left/right/down, Right Shift for fire
# (You can rebind under Settings → Input devices → Joystick settings.)
# #
# Optimization: the default is oscar64's default (-O1). Pass -O3 to # Optimization: the default is oscar64's default (-O1). Pass -O3 to
# produce a release build (auto-zero-page, outliner, aggressive inlining). # produce a release build (auto-zero-page, outliner, aggressive inlining).
# The release build is functionally identical to the default build on # The release build is functionally identical to the default build on
# cycle-accurate timings — the audio and frame counters are still # cycle-accurate timings — the audio and frame counters are still
# 50 Hz because they use the raster IRQ, not CPU-bound loops. # 50 Hz because they use the raster IRQ, not CPU-bound loops.
#
# Both builds pin `#pragma stacksize(0x400)` in main.c. At -O3 the
# code section is ~70% larger, so the data section's spillover into
# the default stack/heap gap region leaves only ~0x400 bytes for
# both — anything larger fails to link. 1 KB is enough because
# the oscar64 software stack lives in zero-page (0xF7-0xFF); the
# spillover area only needs to hold the few locals/params that
# don't fit in ZP.
set -e set -e
# --- locate the oscar64 compiler ----------------------------------------- # --- locate the oscar64 compiler -----------------------------------------
# This script is ./src/build.sh, so the repo root is one level up.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
OSCAR64_DIR="$ROOT/oscar64" OSCAR64_DIR="$ROOT/oscar64"
OSCAR64_BIN="$OSCAR64_DIR/bin/oscar64" OSCAR64_BIN="$OSCAR64_DIR/bin/oscar64"
BUILD_DIR="$SCRIPT_DIR/build" BUILD_DIR="$SCRIPT_DIR/build"
PRG="$BUILD_DIR/whack_hare.prg"
D64="$BUILD_DIR/whack.d64"
# Output goes in ./build/ (relative to the src dir), kept out of the source tree.
mkdir -p "$BUILD_DIR" mkdir -p "$BUILD_DIR"
# Build the compiler if the binary is missing. (make -C make is the # --- kill any running VICE ------------------------------------------------
# makefile-based build from the upstream oscar64 source tree.) if [ "${1:-}" = "--kill" ]; then
pids=$(pgrep -f "x64sc? +confirmonexit.*whack\.d64" 2>/dev/null || true)
if [ -n "$pids" ]; then
echo "killing VICE process(es): $pids"
kill $pids 2>/dev/null || true
sleep 1
kill -9 $pids 2>/dev/null || true
else
echo "no VICE process running (autostart of whack.d64)"
fi
exit 0
fi
# Build the compiler if it's missing.
if [ ! -x "$OSCAR64_BIN" ]; then if [ ! -x "$OSCAR64_BIN" ]; then
echo "oscar64 compiler not found at $OSCAR64_BIN; building it..." echo "oscar64 compiler not found at $OSCAR64_BIN; building it..."
( cd "$OSCAR64_DIR" && make -C make compiler ) ( cd "$OSCAR64_DIR" && make -C make compiler )
@@ -64,20 +79,20 @@ if [ ! -x "$OSCAR64_BIN" ]; then
exit 1 exit 1
fi fi
# --- compile + optionally run -------------------------------------------- # --- parse args -----------------------------------------------------------
# Phase 2+ entry point. helloworld.c is kept around as a minimal sanity
# check; build it explicitly with `./build.sh -c helloworld` (TODO if
# needed). Default is the real game.
SRC=main.c SRC=main.c
EMU_CMD="" EMU_CMD=""
PLAY_VICE=""
OPT_FLAGS="" OPT_FLAGS=""
for arg in "$@"; do for arg in "$@"; do
case "$arg" in case "$arg" in
-e) EMU_CMD="oscar64" ;; # oscar64's built-in emulator (headless) -e) EMU_CMD="oscar64" ;;
-v) EMU_CMD="x64" ;; # VICE standard -v) EMU_CMD="x64" ;;
-V) EMU_CMD="x64sc" ;; # VICE cycle-exact -V) EMU_CMD="x64sc" ;;
-c) ;; # explicit compile-only -p) PLAY_VICE="x64" ;;
-P) PLAY_VICE="x64sc" ;;
-c) ;;
-O0) OPT_FLAGS="$OPT_FLAGS -O0" ;; -O0) OPT_FLAGS="$OPT_FLAGS -O0" ;;
-O1) OPT_FLAGS="$OPT_FLAGS -O1" ;; -O1) OPT_FLAGS="$OPT_FLAGS -O1" ;;
-O2) OPT_FLAGS="$OPT_FLAGS -O2" ;; -O2) OPT_FLAGS="$OPT_FLAGS -O2" ;;
@@ -88,31 +103,67 @@ for arg in "$@"; do
esac esac
done done
# --- compile --------------------------------------------------------------
echo "compiling $SRC with $OSCAR64_BIN -> $BUILD_DIR/" echo "compiling $SRC with $OSCAR64_BIN -> $BUILD_DIR/"
# -o puts the .prg in the build dir; the other artifacts (.asm, .map, .lbl) "$OSCAR64_BIN" -i="$OSCAR64_DIR/include" -o="$PRG" $OPT_FLAGS "$SRC"
# follow automatically since they share the base name.
"$OSCAR64_BIN" -i="$OSCAR64_DIR/include" -o="$BUILD_DIR/whack_hare.prg" $OPT_FLAGS "$SRC"
# --- optional actions -----------------------------------------------------
case "$EMU_CMD" in case "$EMU_CMD" in
"")
# compile only
;;
"oscar64") "oscar64")
echo "running whack_hare.prg in oscar64's built-in emulator" echo "running whack_hare.prg in oscar64's built-in emulator"
"$OSCAR64_BIN" -i="$OSCAR64_DIR/include" -o="$BUILD_DIR/whack_hare.prg" $OPT_FLAGS -e "$SRC" "$OSCAR64_BIN" -i="$OSCAR64_DIR/include" -o="$PRG" $OPT_FLAGS -e "$SRC"
;; ;;
"x64"|"x64sc") "x64"|"x64sc")
if ! command -v "$EMU_CMD" >/dev/null 2>&1; then echo "wrapping $PRG in $D64 (VICE autostart needs a disk image)..."
echo "error: $EMU_CMD not found in PATH" >&2 if ! command -v c1541 >/dev/null 2>&1; then
echo "error: c1541 not found (needed to build the .d64 wrapper)" >&2
exit 1 exit 1
fi fi
c1541 -format wh,of d64 "$D64" >/dev/null
c1541 "$D64" >/dev/null <<EOF
write $PRG
EOF
if [ -z "${DISPLAY:-}" ]; then if [ -z "${DISPLAY:-}" ]; then
echo "warning: no \$DISPLAY set; VICE may not render correctly" >&2 echo "warning: no \$DISPLAY set; VICE won't show a window" >&2
echo " for headless testing use -e (oscar64's built-in emulator)" >&2
fi fi
echo "running whack_hare.prg in VICE ($EMU_CMD)" echo "running whack_hare in VICE ($EMU_CMD, blocking)"
"$EMU_CMD" "$BUILD_DIR/whack_hare.prg" "$EMU_CMD" +confirmonexit -autostart "$D64"
;; ;;
esac esac
echo "done: $BUILD_DIR/whack_hare.prg" if [ -n "$PLAY_VICE" ]; then
if [ -z "${DISPLAY:-}" ]; then
echo "error: no \$DISPLAY set; cannot launch VICE in a window" >&2
exit 1
fi
if ! command -v "$PLAY_VICE" >/dev/null 2>&1; then
echo "error: $PLAY_VICE not found in PATH" >&2
exit 1
fi
echo "wrapping $PRG in $D64 (VICE autostart needs a disk image)..."
c1541 -format wh,of d64 "$D64" >/dev/null
c1541 "$D64" >/dev/null <<EOF
write $PRG
EOF
echo "launching VICE $PLAY_VICE in the background with $D64..."
log="$BUILD_DIR/vice.log"
( setsid nohup "$PLAY_VICE" +confirmonexit -autostart "$D64" \
> "$log" 2>&1 < /dev/null & echo $! > "$BUILD_DIR/vice.pid" )
# setsid + nohup detach VICE from this shell so it survives our exit.
pid=$(cat "$BUILD_DIR/vice.pid")
sleep 1
if kill -0 "$pid" 2>/dev/null; then
echo ""
echo "VICE launched (PID $pid). Look for the x64 window on your desktop."
echo "Default keys: Port 1 (Hare) = Arrows + Right Shift (fire)"
echo " Port 2 (Scoot) = W A S D + Left Ctrl (fire)"
echo "Rebind under Settings → Input devices → Joystick settings."
echo "To stop VICE later: ./build.sh --kill"
echo "Log file: $log"
else
echo "error: VICE failed to start; see $log for details" >&2
exit 1
fi
fi
echo "done: $PRG"