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:
@@ -355,7 +355,6 @@ src/
|
||||
├── draw.c / .h # the white-screen + counter rendering
|
||||
├── rasterirq.c / .h # 50 Hz frame tick
|
||||
├── notes.h # SID frequency table
|
||||
├── build.sh # (already exists)
|
||||
└── build/ # (gitignored) output
|
||||
```
|
||||
|
||||
|
||||
@@ -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/"
|
||||
@@ -12,8 +12,8 @@ as the cross-compiler. Oscar64 is checked in as a git submodule under
|
||||
├── build/ # Build output (gitignored)
|
||||
├── docs/c64/ # Low-level C64 reference (memory map, VIC, CIA, SID, …)
|
||||
├── src/ # Your C code
|
||||
│ ├── helloworld.c
|
||||
│ └── build.sh # Compile + run helper
|
||||
│ └── *.c, *.h
|
||||
├── Makefile # Build + run targets (gnu make)
|
||||
├── OSCAR64.md # Notes on the Oscar64 compiler internals
|
||||
└── PROG_C64.md # Notes on programming the C64 hardware
|
||||
```
|
||||
@@ -31,28 +31,28 @@ git submodule update --init --recursive
|
||||
## Building
|
||||
|
||||
```sh
|
||||
cd src
|
||||
./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: launch VICE in background (see build.sh --help)
|
||||
./build.sh -v # run in VICE x64 (foreground, blocks terminal)
|
||||
./build.sh -V # run in VICE x64sc (cycle-exact, foreground)
|
||||
./build.sh --kill # kill any detached VICE process
|
||||
./build.sh -c # just compile
|
||||
./build.sh -O3 # release build (auto-ZP, outliner, aggressive inlining)
|
||||
make # show help with all targets
|
||||
make compile # compile → build/nyuller.prg (-O1)
|
||||
make run # compile + run in oscar64 emulator (headless, fast)
|
||||
make play # compile + launch VICE x64 in background
|
||||
make kill # kill any detached VICE
|
||||
make OPT=O3 # release build (auto-ZP, outliner, aggressive inlining)
|
||||
make clean # remove build/ artifacts
|
||||
```
|
||||
|
||||
`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
|
||||
`./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`
|
||||
step in `tasks.md` uses this.
|
||||
|
||||
**VICE 3.9 is installed** at `/usr/bin/` (`x64`, `x64sc`, `x128`,
|
||||
`xvic`, `xpet`) and is available via `-v` / `-V`. It is a GUI
|
||||
emulator and **needs a real X11 / Wayland display to render** — it
|
||||
`xvic`, `xpet`) and is available via `make run-vice` / `make run-vice-cycle`.
|
||||
It is a GUI emulator and **needs a real X11 / Wayland display to render** — it
|
||||
won't produce useful screenshots in this headless environment.
|
||||
Use it from a real terminal session for interactive play-testing
|
||||
and cycle-exact validation of raster IRQ and SID timing; don't
|
||||
|
||||
+13
-12
@@ -28,29 +28,30 @@ context you need to do the work.
|
||||
├── source_images/ # The 5 source PNGs for the game screens
|
||||
└── src/ # YOUR CODE GOES HERE
|
||||
├── 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 and test (this is the ONLY way to verify your work)
|
||||
|
||||
```sh
|
||||
cd /home/ballz/work/teletype/nyuller/src
|
||||
./build.sh # compile helloworld.c → build/helloworld.prg
|
||||
./build.sh -e # run in oscar64's built-in emulator (HEADLESS, fast)
|
||||
./build.sh -v # (DO NOT USE) VICE x64 — requires a real display
|
||||
./build.sh -V # (DO NOT USE) VICE x64sc — same problem
|
||||
cd /home/ballz/work/teletype/nyuller
|
||||
make # show help with all targets
|
||||
make compile # compile → build/nyuller.prg
|
||||
make run # compile + run in oscar64 emulator (HEADLESS, fast)
|
||||
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
|
||||
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.
|
||||
|
||||
`build.sh` 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`
|
||||
to compile, and the same command with `-e` to run.
|
||||
`make` will auto-build the oscar64 compiler if it's missing.
|
||||
It runs `cd src && oscar64 -i=…/include -o=…/build/nyuller.prg main.c`
|
||||
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),
|
||||
`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:
|
||||
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
|
||||
4. Any concerns or follow-up work for the next phase
|
||||
|
||||
-217
@@ -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
@@ -2,9 +2,8 @@
|
||||
//
|
||||
// Builds a .prg that prints "Hello World" on the C64 text screen and exits.
|
||||
//
|
||||
// Build: ../scripts/build.sh
|
||||
// Run: x64 helloworld.prg (or use ../scripts/build.sh -e to run in
|
||||
// the built-in oscar64 emulator)
|
||||
// Build: make compile
|
||||
// Run: make run
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ produces files in `./src/data/`.
|
||||
## 0.1. Test environment
|
||||
|
||||
**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
|
||||
display, no ROMs, and at high speed. It is the default for the
|
||||
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,
|
||||
and the autostart mechanism produced blank screenshots (no display
|
||||
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
|
||||
has a real terminal session and wants to use VICE interactively,
|
||||
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 |
|
||||
|------|-----------|------------|
|
||||
| `build.sh -e` (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` (oscar64 built-in) | Yes | Default for every Verify step. Fast, deterministic, runs in CI. |
|
||||
| `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). |
|
||||
|
||||
**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
|
||||
(end-to-end testing) and in the optional Phase 9 (NTSC) — both
|
||||
of which assume a developer with a real terminal session will run
|
||||
@@ -64,11 +64,11 @@ the tests.
|
||||
runs.
|
||||
|
||||
- [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`.
|
||||
- [x] ✅ `GAME.md` written.
|
||||
- [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
|
||||
`x64 build/helloworld.prg` in a real terminal session
|
||||
to see the screen.)
|
||||
@@ -168,7 +168,7 @@ state.
|
||||
pressed (active low: `(PEEK(0xDC00+port) & 0x10) == 0`).
|
||||
|
||||
**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)
|
||||
then exits.
|
||||
- (Optional, interactive) `x64 build/nyuller.prg` in a real
|
||||
@@ -303,7 +303,7 @@ random source.
|
||||
|
||||
**Verify:**
|
||||
- The game still works in the oscar64 built-in emulator
|
||||
(`build.sh -e`).
|
||||
(`make run`).
|
||||
- (Optional, interactive) Launch `x64sc build/nyuller.prg`
|
||||
in a real terminal to confirm the game runs at cycle-exact PAL
|
||||
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.
|
||||
- [ ] Make sure scores are reset on entering GAMEOVER → TITLE.
|
||||
- [ ] 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
|
||||
interactive play-testing and cycle-exact validation. Coverage:
|
||||
- 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
|
||||
real hardware. Otherwise document that we tested in the
|
||||
oscar64 emulator + `x64sc` cycle-exact mode.
|
||||
- [ ] Strip `-g` from the release build. Add a `-O3` build
|
||||
target to build.sh.
|
||||
- [ ] Strip `-g` from the release build. Add a `make OPT=O3` build
|
||||
target to Makefile (already supported via `make compile OPT=O3`).
|
||||
- [ ] Final pass: review the .map file, check no section is
|
||||
larger than expected, check no RAM region is over-allocated.
|
||||
- [ ] Final sanity check: load the release build into `x64sc`
|
||||
|
||||
Reference in New Issue
Block a user