Replace src/build.sh with Makefile (GNU make)

New targets:
  help (default)     — show usage (was the -help/--help equivalent)
  compile            — compile main.c → build/nyuller.prg
  run                — compile + run in oscar64 built-in emulator
  run-vice           — compile + run in VICE x64 (foreground)
  run-vice-cycle     — compile + run in VICE x64sc (cycle-exact)
  play               — compile + launch VICE x64 detached
  play-cycle         — compile + launch VICE x64sc detached
  kill               — kill any detached VICE
  clean              — remove build/ artifacts
  build/nyuller.d64  — build the .d64 disk image from the .prg

Optimization: override with 'make OPT=O3' (or O0/O1/O2/Os/g).
Default is -O1 (oscar64 default).

The Makefile builds oscar64 automatically if missing, uses
c1541 to create the .d64 with verification, and passes
-drive8type 1541 to VICE (required for autostart).

Removed src/build.sh and updated all docs (README, tasks.md,
AGENT_CONTEXT.md, GAME.md, helloworld.c) to reference make.
This commit is contained in:
ballz
2026-07-18 16:22:38 +02:00
parent 3e9683a2fc
commit b7219dccfc
7 changed files with 276 additions and 260 deletions
-1
View File
@@ -355,7 +355,6 @@ src/
├── draw.c / .h # the white-screen + counter rendering ├── draw.c / .h # the white-screen + counter rendering
├── rasterirq.c / .h # 50 Hz frame tick ├── rasterirq.c / .h # 50 Hz frame tick
├── notes.h # SID frequency table ├── notes.h # SID frequency table
├── build.sh # (already exists)
└── build/ # (gitignored) output └── build/ # (gitignored) output
``` ```
+234
View File
@@ -0,0 +1,234 @@
# nyuller — C64 game (Oscar64 cross-compiler)
# ============================================================
# GNU Make wrapper for building and running via oscar64 / VICE.
#
# Default target: help
# --- paths -------------------------------------------------
ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
OSCAR64_DIR := $(ROOT)/oscar64
OSCAR64_BIN := $(OSCAR64_DIR)/bin/oscar64
BUILD_DIR := $(ROOT)/build
SRC := main.c
PRG := $(BUILD_DIR)/nyuller.prg
D64 := $(BUILD_DIR)/nyuller.d64
LOG := $(BUILD_DIR)/vice.log
PID_FILE := $(BUILD_DIR)/vice.pid
SRC_DIR := $(ROOT)/src
# --- oscar64 flags ----------------------------------------
# Override with: make OPT=O3 (or O0/O1/O2/Os/g)
OPT ?= O1
OPT_FLAGS := -$(OPT)
# --- VICE flags -------------------------------------------
VICE_FLAGS := +confirmonexit -drive8type 1541
# --- phony targets ----------------------------------------
.PHONY: help compile run run-vice run-vice-cycle-exact \
play play-cycle-exact kill clean ensure-build-dir
# ============================================================
# help (default)
# ============================================================
help:
@echo ""
@echo " nyuller — C64 game (Oscar64 cross-compiler)"
@echo " ──────────────────────────────────────────────"
@echo ""
@echo " Compile"
@echo " make compile main.c → build/nyuller.prg (-O1)"
@echo " make compile same as above"
@echo " make OPT=O3 compile with -O3 (release build)"
@echo " make OPT=O0 compile with -O0 (debug build)"
@echo " make OPT=Os compile with -Os (optimize for size)"
@echo ""
@echo " Run"
@echo " make run compile + run in oscar64 emulator (headless, fast)"
@echo " make run-vice compile + run in VICE x64 (foreground, blocks)"
@echo " make run-vice-cycle compile + run in VICE x64sc (cycle-exact, slow)"
@echo ""
@echo " Play (detached — returns to shell)"
@echo " make play compile + launch VICE x64 in background"
@echo " make play-cycle compile + launch VICE x64sc in background"
@echo " make kill kill any running detached VICE"
@echo ""
@echo " Clean"
@echo " make clean remove build/ artifacts and VICE log/pid files"
@echo ""
@echo " Output (in build/)"
@echo " nyuller.prg loadable C64 program"
@echo " nyuller.d64 disk image wrapping nyuller.prg (for VICE autostart)"
@echo " nyuller.asm full 6502 listing"
@echo " nyuller.map region/section/object placement"
@echo " nyuller.lbl VICE monitor label commands"
@echo ""
@echo " VICE note: -drive8type 1541 is required for autostart to work."
@echo " Without this flag the autostart LOAD\"*\",8,1 fails with ?DEVICE"
@echo " NOT PRESENT. We have the original 1541 ROM installed at:"
@echo " ~/.local/share/vice/DRIVES/dos1541-325302-01+901229-05.bin"
@echo ""
@echo " Default joystick keys in VICE (rebindable under Settings):"
@echo " Port 2 (left, Scoot) W/A/S/D + Left Ctrl (fire)"
@echo " Port 1 (right, Hare) Arrows + Right Shift (fire)"
@echo ""
# ============================================================
# ensure-build-dir
# ============================================================
ensure-build-dir:
@mkdir -p "$(BUILD_DIR)"
# ============================================================
# compile
# ============================================================
compile: ensure-build-dir
@# Build the oscar64 compiler first if it doesn't exist.
@if [ ! -x "$(OSCAR64_BIN)" ]; then \
echo "oscar64 compiler not found at $(OSCAR64_BIN); building it..."; \
cd "$(OSCAR64_DIR)" && make -C make compiler; \
fi
@if [ ! -x "$(OSCAR64_BIN)" ]; then \
echo "error: $(OSCAR64_BIN) is still missing after build" >&2; \
echo " try: cd $(OSCAR64_DIR) && make -C make compiler" >&2; \
exit 1; \
fi
@echo "compiling $(SRC) with $(OSCAR64_BIN) -> $(BUILD_DIR)/"
cd "$(SRC_DIR)" && "$(OSCAR64_BIN)" -i="$(OSCAR64_DIR)/include" -o="$(PRG)" $(OPT_FLAGS) "$(SRC)"
# ============================================================
# run — oscar64 built-in emulator (headless, fast)
# ============================================================
run: ensure-build-dir
@if [ ! -x "$(OSCAR64_BIN)" ]; then \
echo "oscar64 compiler not found at $(OSCAR64_BIN); building it..."; \
cd "$(OSCAR64_DIR)" && make -C make compiler; \
fi
@if [ ! -x "$(OSCAR64_BIN)" ]; then \
echo "error: $(OSCAR64_BIN) is still missing after build" >&2; \
echo " try: cd $(OSCAR64_DIR) && make -C make compiler" >&2; \
exit 1; \
fi
@echo "running $(SRC) in oscar64's built-in emulator"
cd "$(SRC_DIR)" && "$(OSCAR64_BIN)" -i="$(OSCAR64_DIR)/include" -o="$(PRG)" $(OPT_FLAGS) -e "$(SRC)"
# ============================================================
# run-vice — VICE x64 (foreground, blocks terminal)
# ============================================================
run-vice: $(D64)
@echo "running nyuller in VICE x64 (blocking)"
x64 $(VICE_FLAGS) -autostart "$(D64)"
# ============================================================
# run-vice-cycle — VICE x64sc (foreground, cycle-exact, slow)
# ============================================================
run-vice-cycle: $(D64)
@echo "running nyuller in VICE x64sc (cycle-exact, blocking)"
x64sc $(VICE_FLAGS) -autostart "$(D64)"
# ============================================================
# play — VICE x64 detached (returns to shell)
# ============================================================
play: compile $(D64)
@# Kill any previous VICE first.
@existing=$$(pgrep -f 'x64(sc)? \+confirmonexit.*nyuller\.d64' 2>/dev/null || true); \
if [ -n "$$existing" ]; then \
echo "killing previous VICE process(es): $$existing"; \
kill $$existing 2>/dev/null || true; \
sleep 1; \
kill -9 $$existing 2>/dev/null || true; \
fi
@echo "launching VICE x64 in the background with $(D64)..."
@setsid nohup x64 $(VICE_FLAGS) -autostart "$(D64)" \
> "$(LOG)" 2>&1 < /dev/null & echo $$! > "$(PID_FILE)"
@sleep 1
@if kill -0 "$$(cat "$(PID_FILE)")" 2>/dev/null; then \
echo ""; \
echo "VICE launched (PID $$(cat "$(PID_FILE)")). 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: make kill"; \
echo "Log file: $(LOG)"; \
else \
echo "error: VICE failed to start; see $(LOG) for details" >&2; \
exit 1; \
fi
# ============================================================
# play-cycle — VICE x64sc detached (cycle-exact, returns to shell)
# ============================================================
play-cycle: compile $(D64)
@existing=$$(pgrep -f 'x64(sc)? \+confirmonexit.*nyuller\.d64' 2>/dev/null || true); \
if [ -n "$$existing" ]; then \
echo "killing previous VICE process(es): $$existing"; \
kill $$existing 2>/dev/null || true; \
sleep 1; \
kill -9 $$existing 2>/dev/null || true; \
fi
@echo "launching VICE x64sc in the background with $(D64)..."
@setsid nohup x64sc $(VICE_FLAGS) -autostart "$(D64)" \
> "$(LOG)" 2>&1 < /dev/null & echo $$! > "$(PID_FILE)"
@sleep 1
@if kill -0 "$$(cat "$(PID_FILE)")" 2>/dev/null; then \
echo ""; \
echo "VICE launched (PID $$(cat "$(PID_FILE)")). Look for the x64sc 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: make kill"; \
echo "Log file: $(LOG)"; \
else \
echo "error: VICE failed to start; see $(LOG) for details" >&2; \
exit 1; \
fi
# ============================================================
# kill — stop any detached VICE
# ============================================================
kill:
@# pgrep -f uses ERE: + is a quantifier, so the regex must escape it.
@# x64(sc)? matches both "x64" and "x64sc". The process is launched
@# with the literal argument string `+confirmonexit`.
@pids=$$(pgrep -f 'x64(sc)? \+confirmonexit.*nyuller\.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 nyuller.d64)"; \
fi
@rm -f "$(PID_FILE)" "$(LOG)"
# ============================================================
# $(D64) — build the .d64 disk image from the .prg
# ============================================================
# c1541's exit code is unreliable (the interactive form always
# exits 0 even on failure), so the only reliable success signal
# is listing the disk afterwards and checking the PRG is there.
# The flag-form `c1541 -list image` doesn't work (c1541 treats
# it as a unit number), so we use the interactive heredoc form.
# All c1541 calls are wrapped in `|| true` so the final
# list-and-grep is the authoritative check.
$(D64): $(PRG) | ensure-build-dir
@echo "wrapping $(PRG) in $(D64) (VICE autostart needs a disk image)..."
@c1541 -format ny,of d64 "$(D64)" >/dev/null 2>&1 || true
@echo "write $(PRG)" | c1541 "$(D64)" >/dev/null 2>&1 || true
@if printf "list\nquit\n" | c1541 "$(D64)" 2>/dev/null | grep -q "nyuller.prg"; then \
:; \
else \
echo "error: failed to write $(PRG) to $(D64) (c1541 silently exited 0)" >&2; \
rm -f "$(D64)"; \
exit 1; \
fi
# ============================================================
# clean
# ============================================================
clean:
rm -f "$(PRG)" "$(D64)" "$(BUILD_DIR)/nyuller.asm" "$(BUILD_DIR)/nyuller.map" \
"$(BUILD_DIR)/nyuller.lbl" "$(BUILD_DIR)/nyuller.int" "$(BUILD_DIR)/nyuller.dbj" \
"$(BUILD_DIR)/nyuller.csz" "$(PID_FILE)" "$(LOG)"
@echo "cleaned build/"
+15 -15
View File
@@ -12,8 +12,8 @@ as the cross-compiler. Oscar64 is checked in as a git submodule under
├── build/ # Build output (gitignored) ├── build/ # Build output (gitignored)
├── docs/c64/ # Low-level C64 reference (memory map, VIC, CIA, SID, …) ├── docs/c64/ # Low-level C64 reference (memory map, VIC, CIA, SID, …)
├── src/ # Your C code ├── src/ # Your C code
── helloworld.c ── *.c, *.h
│ └── build.sh # Compile + run helper ├── Makefile # Build + run targets (gnu make)
├── OSCAR64.md # Notes on the Oscar64 compiler internals ├── OSCAR64.md # Notes on the Oscar64 compiler internals
└── PROG_C64.md # Notes on programming the C64 hardware └── PROG_C64.md # Notes on programming the C64 hardware
``` ```
@@ -31,28 +31,28 @@ git submodule update --init --recursive
## Building ## Building
```sh ```sh
cd src make # show help with all targets
./build.sh # compile main.c → build/nyuller.prg (-O1) make compile # compile → build/nyuller.prg (-O1)
./build.sh -e # run in oscar64's built-in emulator (headless, fast) make run # compile + run in oscar64 emulator (headless, fast)
./build.sh -p # PLAY: launch VICE in background (see build.sh --help) make play # compile + launch VICE x64 in background
./build.sh -v # run in VICE x64 (foreground, blocks terminal) make kill # kill any detached VICE
./build.sh -V # run in VICE x64sc (cycle-exact, foreground) make OPT=O3 # release build (auto-ZP, outliner, aggressive inlining)
./build.sh --kill # kill any detached VICE process make clean # remove build/ artifacts
./build.sh -c # just compile
./build.sh -O3 # release build (auto-ZP, outliner, aggressive inlining)
``` ```
`build.sh` will build the oscar64 compiler automatically the first time `make` will build the oscar64 compiler automatically the first time
(it runs `make -C make compiler` inside `./oscar64/` if (it runs `make -C make compiler` inside `./oscar64/` if
`./oscar64/bin/oscar64` doesn't exist yet). `./oscar64/bin/oscar64` doesn't exist yet).
**Default test tool is the oscar64 built-in emulator** (`-e`): it Run `make help` for the full list of targets with descriptions.
**Default test tool is the oscar64 built-in emulator** (`make run`): it
runs headless, needs no ROMs, no display, and is fast. Every `Verify` runs headless, needs no ROMs, no display, and is fast. Every `Verify`
step in `tasks.md` uses this. step in `tasks.md` uses this.
**VICE 3.9 is installed** at `/usr/bin/` (`x64`, `x64sc`, `x128`, **VICE 3.9 is installed** at `/usr/bin/` (`x64`, `x64sc`, `x128`,
`xvic`, `xpet`) and is available via `-v` / `-V`. It is a GUI `xvic`, `xpet`) and is available via `make run-vice` / `make run-vice-cycle`.
emulator and **needs a real X11 / Wayland display to render** — it It is a GUI emulator and **needs a real X11 / Wayland display to render** — it
won't produce useful screenshots in this headless environment. won't produce useful screenshots in this headless environment.
Use it from a real terminal session for interactive play-testing Use it from a real terminal session for interactive play-testing
and cycle-exact validation of raster IRQ and SID timing; don't and cycle-exact validation of raster IRQ and SID timing; don't
+13 -12
View File
@@ -28,29 +28,30 @@ context you need to do the work.
├── source_images/ # The 5 source PNGs for the game screens ├── source_images/ # The 5 source PNGs for the game screens
└── src/ # YOUR CODE GOES HERE └── src/ # YOUR CODE GOES HERE
├── helloworld.c # Existing minimal working program ├── helloworld.c # Existing minimal working program
├── build.sh # Build script (handles compiler build, runs tests) ├── build.sh # Legacy build script (replaced by Makefile)
└── build/ # Output directory (gitignored) └── build/ # Output directory (gitignored)
``` ```
## Build and test (this is the ONLY way to verify your work) ## Build and test (this is the ONLY way to verify your work)
```sh ```sh
cd /home/ballz/work/teletype/nyuller/src cd /home/ballz/work/teletype/nyuller
./build.sh # compile helloworld.c → build/helloworld.prg make # show help with all targets
./build.sh -e # run in oscar64's built-in emulator (HEADLESS, fast) make compile # compile → build/nyuller.prg
./build.sh -v # (DO NOT USE) VICE x64 — requires a real display make run # compile + run in oscar64 emulator (HEADLESS, fast)
./build.sh -V # (DO NOT USE) VICE x64sc — same problem make play # compile + launch VICE in background
make kill # kill any detached VICE
``` ```
**The oscar64 built-in emulator (`-e`) is the only test tool.** VICE **The oscar64 built-in emulator (`make run`) is the only test tool.** VICE
does not work in this headless environment (no display, blank does not work in this headless environment (no display, blank
screenshots, manual ROM fetching). Do not waste time on VICE. screenshots, manual ROM fetching). Do not waste time on VICE.
The `-e` flag runs the same `.prg` file the C64 will run, with no The `run` target runs the same `.prg` file the C64 will run, with no
setup, no ROMs, and at high speed. setup, no ROMs, and at high speed.
`build.sh` will auto-build the oscar64 compiler if it's missing. `make` will auto-build the oscar64 compiler if it's missing.
It calls `oscar64 -i=/home/ballz/work/teletype/nyuller/oscar64/include -o=build/helloworld.prg helloworld.c` It runs `cd src && oscar64 -i=…/include -o=…/build/nyuller.prg main.c`
to compile, and the same command with `-e` to run. to compile, and `cd src && oscar64 -i=…/include -o=…/build/nyuller.prg -e main.c` to run.
The build artifacts in `build/` are: `helloworld.prg` (the C64 program), The build artifacts in `build/` are: `helloworld.prg` (the C64 program),
`helloworld.asm` (6502 listing), `helloworld.map` (region/section/object `helloworld.asm` (6502 listing), `helloworld.map` (region/section/object
@@ -180,6 +181,6 @@ $21-$24) goes to $D800-$DBE7.
When you're done, report back: When you're done, report back:
1. What you built (1-2 sentence summary) 1. What you built (1-2 sentence summary)
2. The output of `./build.sh -e` (proves it compiled and runs) 2. The output of `make run` (proves it compiled and runs)
3. The git commit hash and one-line summary 3. The git commit hash and one-line summary
4. Any concerns or follow-up work for the next phase 4. Any concerns or follow-up work for the next phase
-217
View File
@@ -1,217 +0,0 @@
#!/bin/sh
# build.sh — compile and optionally run a single C64 program with Oscar64.
#
# Usage:
# ./build.sh # compile main.c → build/nyuller.prg (-O1)
# ./build.sh -e # run in oscar64's built-in emulator (headless, fast)
# ./build.sh -p # PLAY: compile, wrap in .d64, launch VICE x64 detached
# ./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 -O0 # compile with -O0 (no optimization; debug build)
# ./build.sh -O2 # compile with -O2 (more aggressive inlining)
# ./build.sh -O3 # compile with -O3 (release build; auto-ZP, outliner)
# ./build.sh -Os # compile with -Os (optimize for size)
# ./build.sh -g # compile with -g (adds source-level debug info)
#
# Output (in <repo>/build/):
# nyuller.prg — loadable C64 program (run with x64, VICE, or real hw)
# nyuller.asm — full 6502 listing
# nyuller.map — region/section/object placement
# nyuller.lbl — VICE monitor label commands
# nyuller.d64 — disk image wrapping nyuller.prg (for VICE autostart)
#
# VICE note: -drive8type 1541 is required for autostart to work. VICE
# defaults to the 1541-II (drive type 1542), whose ROM we don't have
# installed; without this flag the autostart `LOAD"*",8,1` fails with
# ?DEVICE NOT PRESENT. We have the original 1541 ROM at
# ~/.local/share/vice/DRIVES/dos1541-325302-01+901229-05.bin.
#
# For the development loop, use -e (oscar64's built-in emulator). It runs
# without a display, needs no ROMs, and is faster than VICE. Use -p when
# you want to play the game interactively — it launches VICE in the
# 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
# produce a release build (auto-zero-page, outliner, aggressive inlining).
# The release build is functionally identical to the default build on
# cycle-accurate timings — the audio and frame counters are still
# 50 Hz because they use the raster IRQ, not CPU-bound loops.
set -e
# --- locate the oscar64 compiler -----------------------------------------
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
OSCAR64_DIR="$ROOT/oscar64"
OSCAR64_BIN="$OSCAR64_DIR/bin/oscar64"
BUILD_DIR="$ROOT/build"
PRG="$BUILD_DIR/nyuller.prg"
D64="$BUILD_DIR/nyuller.d64"
mkdir -p "$BUILD_DIR"
# --- kill any running VICE ------------------------------------------------
if [ "${1:-}" = "--kill" ]; then
# pgrep -f uses ERE: + is a quantifier, so the regex must escape it.
# x64(sc)? matches both "x64" and "x64sc". The process is launched
# with the literal argument string `+confirmonexit`.
pids=$(pgrep -f "x64(sc)? \+confirmonexit.*nyuller\.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 nyuller.d64)"
fi
rm -f "$BUILD_DIR/vice.pid" "$BUILD_DIR/vice.log"
exit 0
fi
# Build the compiler if it's missing.
if [ ! -x "$OSCAR64_BIN" ]; then
echo "oscar64 compiler not found at $OSCAR64_BIN; building it..."
( cd "$OSCAR64_DIR" && make -C make compiler )
fi
if [ ! -x "$OSCAR64_BIN" ]; then
echo "error: $OSCAR64_BIN is still missing after build" >&2
echo " try: cd $OSCAR64_DIR && make -C make compiler" >&2
exit 1
fi
# --- parse args -----------------------------------------------------------
SRC=main.c
EMU_CMD=""
PLAY_VICE=""
OPT_FLAGS=""
for arg in "$@"; do
case "$arg" in
-e) EMU_CMD="oscar64" ;;
-v) EMU_CMD="x64" ;;
-V) EMU_CMD="x64sc" ;;
-p) PLAY_VICE="x64" ;;
-P) PLAY_VICE="x64sc" ;;
-c) ;;
-O0) OPT_FLAGS="$OPT_FLAGS -O0" ;;
-O1) OPT_FLAGS="$OPT_FLAGS -O1" ;;
-O2) OPT_FLAGS="$OPT_FLAGS -O2" ;;
-O3) OPT_FLAGS="$OPT_FLAGS -O3" ;;
-Os) OPT_FLAGS="$OPT_FLAGS -Os" ;;
-g) OPT_FLAGS="$OPT_FLAGS -g" ;;
-*) echo "unknown flag: $arg" >&2; exit 1 ;;
esac
done
# Reject conflicting flag combinations: -v/-V (foreground VICE) and
# -p/-P (background VICE) would both fire, leaving two emulator windows.
if [ -n "$EMU_CMD" ] && [ -n "$PLAY_VICE" ]; then
echo "error: cannot combine a foreground VICE flag (-v/-V) with a play flag (-p/-P)" >&2
echo " use -v to run in the foreground, or -p to detach and continue" >&2
exit 1
fi
# --- compile --------------------------------------------------------------
echo "compiling $SRC with $OSCAR64_BIN -> $BUILD_DIR/"
"$OSCAR64_BIN" -i="$OSCAR64_DIR/include" -o="$PRG" $OPT_FLAGS "$SRC"
# build_d64() — wrap $PRG in $D64. c1541's exit code is unreliable
# (the interactive form always exits 0 even on failure), so the only
# reliable success signal is listing the disk afterwards and checking
# the PRG is there. The flag-form `c1541 -list image` doesn't work
# (c1541 treats it as a unit number), so we use the interactive
# heredoc form. All c1541 calls are wrapped in `|| true` so set -e
# doesn't trip on transient non-zero exits from the format / write
# steps; the final list-and-grep is the authoritative check.
build_d64() {
if ! command -v c1541 >/dev/null 2>&1; then
echo "error: c1541 not found (needed to build the .d64 wrapper)" >&2
return 1
fi
c1541 -format ny,of d64 "$D64" >/dev/null 2>&1 || true
c1541 "$D64" >/dev/null 2>&1 <<EOF || true
write $PRG
EOF
if c1541 "$D64" 2>/dev/null <<EOF | grep -q "nyuller.prg"
list
quit
EOF
then
:
else
echo "error: failed to write $PRG to $D64 (c1541 silently exited 0)" >&2
return 1
fi
}
# --- optional actions -----------------------------------------------------
case "$EMU_CMD" in
"oscar64")
echo "running nyuller.prg in oscar64's built-in emulator"
"$OSCAR64_BIN" -i="$OSCAR64_DIR/include" -o="$PRG" $OPT_FLAGS -e "$SRC"
;;
"x64"|"x64sc")
echo "wrapping $PRG in $D64 (VICE autostart needs a disk image)..."
build_d64
if [ -z "${DISPLAY:-}" ]; then
echo "warning: no \$DISPLAY set; VICE won't show a window" >&2
fi
echo "running nyuller in VICE ($EMU_CMD, blocking)"
"$EMU_CMD" +confirmonexit -drive8type 1541 -autostart "$D64"
;;
esac
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
# If a previous VICE is still running, kill it before launching a
# new one — otherwise the two windows will fight for the same
# display and the old one's nyuller will keep running in the
# background.
existing=$(pgrep -f "x64(sc)? \+confirmonexit.*nyuller\.d64" 2>/dev/null || true)
if [ -n "$existing" ]; then
echo "killing previous VICE process(es): $existing"
kill $existing 2>/dev/null || true
sleep 1
kill -9 $existing 2>/dev/null || true
fi
echo "wrapping $PRG in $D64 (VICE autostart needs a disk image)..."
build_d64
echo "launching VICE $PLAY_VICE in the background with $D64..."
log="$BUILD_DIR/vice.log"
( setsid nohup "$PLAY_VICE" +confirmonexit -drive8type 1541 -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"
+2 -3
View File
@@ -2,9 +2,8 @@
// //
// Builds a .prg that prints "Hello World" on the C64 text screen and exits. // Builds a .prg that prints "Hello World" on the C64 text screen and exits.
// //
// Build: ../scripts/build.sh // Build: make compile
// Run: x64 helloworld.prg (or use ../scripts/build.sh -e to run in // Run: make run
// the built-in oscar64 emulator)
#include <stdio.h> #include <stdio.h>
+12 -12
View File
@@ -17,7 +17,7 @@ produces files in `./src/data/`.
## 0.1. Test environment ## 0.1. Test environment
**The oscar64 built-in emulator is the primary test tool.** It's **The oscar64 built-in emulator is the primary test tool.** It's
invoked with `build.sh -e` (or `oscar64 -i=… -e source.c`) and invoked with `make run` (or `oscar64 -i=… -e source.c`) and
runs the same `.prg` file the C64 will run, with no setup, no runs the same `.prg` file the C64 will run, with no setup, no
display, no ROMs, and at high speed. It is the default for the display, no ROMs, and at high speed. It is the default for the
development loop and is what every phase's *Verify* step uses. development loop and is what every phase's *Verify* step uses.
@@ -28,7 +28,7 @@ In this headless environment, the ROMs had to be fetched manually,
the KERNAL/BASIC/CHAR/1541 ROMs were not bundled with the package, the KERNAL/BASIC/CHAR/1541 ROMs were not bundled with the package,
and the autostart mechanism produced blank screenshots (no display and the autostart mechanism produced blank screenshots (no display
to render to). We attempted to get VICE working and abandoned to render to). We attempted to get VICE working and abandoned
the effort after deciding it wasn't worth the time. The `build.sh the effort after deciding it wasn't worth the time. The `make run`
-v` / `-V` flags remain in place for the rare case where someone -v` / `-V` flags remain in place for the rare case where someone
has a real terminal session and wants to use VICE interactively, has a real terminal session and wants to use VICE interactively,
but **no phase of this project depends on VICE for verification**, but **no phase of this project depends on VICE for verification**,
@@ -36,11 +36,11 @@ and the `-v` / `-V` flags are *optional* additions, not required.
| Tool | Headless? | Use it for | | Tool | Headless? | Use it for |
|------|-----------|------------| |------|-----------|------------|
| `build.sh -e` (oscar64 built-in) | Yes | Default for every Verify step. Fast, deterministic, runs in CI. | | `make run` (oscar64 built-in) | Yes | Default for every Verify step. Fast, deterministic, runs in CI. |
| `build.sh -v` / `-V` (VICE) | No | Optional, interactive only. VICE is unreliable in this headless env and not used for verification. | | `make run-vice` / `make run-vice-cycle` (VICE) | No | Optional, interactive only. VICE is unreliable in this headless env and not used for verification. |
| `x128` / `xvic` / `xpet` | No | Out of scope (we target C64 PAL). | | `x128` / `xvic` / `xpet` | No | Out of scope (we target C64 PAL). |
**Bottom line for the plan:** every `Verify` step uses `build.sh -e` **Bottom line for the plan:** every `Verify` step uses `make run`
(unless explicitly noted). VICE is referenced only in Phase 8 (unless explicitly noted). VICE is referenced only in Phase 8
(end-to-end testing) and in the optional Phase 9 (NTSC) — both (end-to-end testing) and in the optional Phase 9 (NTSC) — both
of which assume a developer with a real terminal session will run of which assume a developer with a real terminal session will run
@@ -64,11 +64,11 @@ the tests.
runs. runs.
- [x] ✅ Repo initialized, oscar64 is a submodule at `./oscar64/`. - [x] ✅ Repo initialized, oscar64 is a submodule at `./oscar64/`.
- [x]`src/helloworld.c` + `src/build.sh` produce - [x] ✅ `src/helloworld.c` + `make compile` produce
`build/helloworld.prg`. `build/helloworld.prg`.
- [x] ✅ `GAME.md` written. - [x] ✅ `GAME.md` written.
- [x] ✅ Source artwork in `./source_images/`. - [x] ✅ Source artwork in `./source_images/`.
- [ ]**Verify:** `cd src && ./build.sh -e` runs the hello-world - [ ] ⏳ **Verify:** `cd src && ./make run` runs the hello-world
program in the oscar64 built-in emulator. (Optional: launch program in the oscar64 built-in emulator. (Optional: launch
`x64 build/helloworld.prg` in a real terminal session `x64 build/helloworld.prg` in a real terminal session
to see the screen.) to see the screen.)
@@ -168,7 +168,7 @@ state.
pressed (active low: `(PEEK(0xDC00+port) & 0x10) == 0`). pressed (active low: `(PEEK(0xDC00+port) & 0x10) == 0`).
**Verify:** **Verify:**
- `cd src && ./build.sh -e` displays the title screen in the - `cd src && ./make run` displays the title screen in the
oscar64 built-in emulator for 5 seconds (or until fire is pressed) oscar64 built-in emulator for 5 seconds (or until fire is pressed)
then exits. then exits.
- (Optional, interactive) `x64 build/nyuller.prg` in a real - (Optional, interactive) `x64 build/nyuller.prg` in a real
@@ -303,7 +303,7 @@ random source.
**Verify:** **Verify:**
- The game still works in the oscar64 built-in emulator - The game still works in the oscar64 built-in emulator
(`build.sh -e`). (`make run`).
- (Optional, interactive) Launch `x64sc build/nyuller.prg` - (Optional, interactive) Launch `x64sc build/nyuller.prg`
in a real terminal to confirm the game runs at cycle-exact PAL in a real terminal to confirm the game runs at cycle-exact PAL
timing (50.125 Hz). The state transitions happen on the right timing (50.125 Hz). The state transitions happen on the right
@@ -432,7 +432,7 @@ with no rough edges. We also do the real-hardware test.
transition. Just a quick low square wave. transition. Just a quick low square wave.
- [ ] Make sure scores are reset on entering GAMEOVER → TITLE. - [ ] Make sure scores are reset on entering GAMEOVER → TITLE.
- [ ] Run the full game in the oscar64 built-in emulator - [ ] Run the full game in the oscar64 built-in emulator
(`build.sh -e`) for the development loop, and (optionally, (`make run`) for the development loop, and (optionally,
when you have a real terminal session) in `x64` / `x64sc` for when you have a real terminal session) in `x64` / `x64sc` for
interactive play-testing and cycle-exact validation. Coverage: interactive play-testing and cycle-exact validation. Coverage:
- P1 wins 5 in a row (cheat test: hold fire on port 1 the - P1 wins 5 in a row (cheat test: hold fire on port 1 the
@@ -449,8 +449,8 @@ with no rough edges. We also do the real-hardware test.
- [ ] If we have a real C64 or a Turbo Everdrive, test on - [ ] If we have a real C64 or a Turbo Everdrive, test on
real hardware. Otherwise document that we tested in the real hardware. Otherwise document that we tested in the
oscar64 emulator + `x64sc` cycle-exact mode. oscar64 emulator + `x64sc` cycle-exact mode.
- [ ] Strip `-g` from the release build. Add a `-O3` build - [ ] Strip `-g` from the release build. Add a `make OPT=O3` build
target to build.sh. target to Makefile (already supported via `make compile OPT=O3`).
- [ ] Final pass: review the .map file, check no section is - [ ] Final pass: review the .map file, check no section is
larger than expected, check no RAM region is over-allocated. larger than expected, check no RAM region is over-allocated.
- [ ] Final sanity check: load the release build into `x64sc` - [ ] Final sanity check: load the release build into `x64sc`