From 8f6d420e3d38e8970ef1f4333918e44058af6e9a Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Mon, 7 Sep 2026 23:20:17 +0200 Subject: [PATCH] CI/CD templates for Commodore Plus/4 projects: the c64-tools set with xplus4 and the plus4-builder image --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++ builder.Dockerfile | 18 ++++++++++++ example-makefile.make | 47 +++++++++++++++++++++++++++++++ example-metadata.json | 10 +++++++ example-tasks.json | 45 ++++++++++++++++++++++++++++++ example-woodpecker.yaml | 5 ++++ 6 files changed, 187 insertions(+) create mode 100644 README.md create mode 100644 builder.Dockerfile create mode 100644 example-makefile.make create mode 100644 example-metadata.json create mode 100644 example-tasks.json create mode 100644 example-woodpecker.yaml diff --git a/README.md b/README.md new file mode 100644 index 0000000..15998df --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# plus4-build + +CI/CD templates for Commodore Plus/4 (ACME + VICE) game projects, in the same +spirit as [c64-tools](../c64-tools): the Plus/4 shares the C64's 6502 core, so +the toolchain is the same ACME assembler — only the emulator (`xplus4`), the +load address (`$1001`) and the TED registers differ. + +- `example-makefile.make` — project Makefile: local dev targets + (`deps`, `build`, `run`, `clear`, `rebuild`, `rebuildrun`). +- `example-woodpecker.yaml` — a one-line marker (`platform: plus4`): the + full pipeline (version → build → artifact → publish) is served by the update server's Woodpecker + configuration extension — preview it with + `curl "https://teletypegames.org/build/config?platform=plus4"`. +- `example-metadata.json` — catalog metadata; the `version` field drives + the pipeline's version step, the rest (`name`, `title`, `author`, `desc`, `site`, + `repo`, `license`) is what `WarpEngine::Platforms::Plus4::Service` writes into + the DB (`site` becomes the "Source Code", `repo` the "Repository" + external link). +- `example-tasks.json` — VS Code `.vscode/tasks.json` wrapping the local + make targets. + +## Usage in a game repo + +1. Copy `example-makefile.make` to `Makefile`, set `PROJECT` (and + `TARGET` if the .prg name differs from `$(PROJECT).prg`). +2. Copy `example-metadata.json` to `metadata.json` and fill it in; + `name` must match `PROJECT`. +3. Copy `example-woodpecker.yaml` to `.woodpecker.yaml` (a one-line marker — the + pipeline itself is served by the update server; add `name:` when the + software name differs from the repo name) and add the + `application_token` secret in Woodpecker (an ApplicationToken with + the `update` + `upload` scopes, created in the admin). +4. Copy `example-tasks.json` to `.vscode/tasks.json`. + +## Plus/4 specifics + +- A BASIC program starts at `$1001` (the C64's is `$0801`), so the SYS stub + and the `* =` origin differ; the entry point after a minimal stub is + `$100D` (`SYS 4109`). +- Screen RAM is at `$0C00`, colour RAM at `$0800`; a colour byte is + `luminance << 4 | hue` (bit 7 flashes). +- Video is the TED at `$FF00`–`$FF3F` (`$FF15` background, `$FF19` border, + `$FF1D`/`$FF1C` raster line); there is no VIC-II, no SID and no CIA. +- The KERNAL jump table is compatible (`$FFD2` CHROUT, `$FFE4` GETIN). + +## Builder image + +`builder.Dockerfile` bakes the pipeline's build-step dependencies +(ACME assembler + make) into `git.teletypegames.org/build/plus4-builder`, +so the build step does not run `apt-get` on every run (VICE is a +local-dev tool only and is not included): + +```sh +docker build --platform linux/amd64 -f builder.Dockerfile \ + -t git.teletypegames.org/build/plus4-builder:latest . +docker push git.teletypegames.org/build/plus4-builder:latest +``` + +The served pipeline uploads `$(PROJECT)-$(VERSION).prg` and +`$(PROJECT)-$(VERSION).metadata.json` via `/build/upload`, then triggers +`/build/publish?platform=plus4&name=$(PROJECT)&version=$(VERSION)`, which +creates/updates the Software row and a Release with `cartridge_path`. diff --git a/builder.Dockerfile b/builder.Dockerfile new file mode 100644 index 0000000..d440b1b --- /dev/null +++ b/builder.Dockerfile @@ -0,0 +1,18 @@ +# CI builder image for Plus/4 projects: ACME assembler + make, so the +# pipeline's build step does not run apt-get on every run. The VICE +# emulator (xplus4) is a local-dev tool only and is intentionally not included. +# +# Build & push (the CI runner is linux/amd64): +# docker build --platform linux/amd64 -f builder.Dockerfile \ +# -t git.teletypegames.org/build/plus4-builder:latest . +# docker push git.teletypegames.org/build/plus4-builder:latest + +FROM debian:stable-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends make acme zip curl ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /workspace + +CMD ["/bin/bash"] diff --git a/example-makefile.make b/example-makefile.make new file mode 100644 index 0000000..11e513f --- /dev/null +++ b/example-makefile.make @@ -0,0 +1,47 @@ +# ----------------------------------------- +# Makefile – Plus/4 project builder (ACME + VICE) +# ----------------------------------------- + +PROJECT = example + +ASM = acme +EMU = xplus4 +SRC = main.asm +SRCS = $(wildcard *.asm) +TARGET ?= $(PROJECT).prg + +METADATA = metadata.json + +UNAME_S := $(shell uname -s) + +# CI/CD variables +VERSION_FILE = .version + +all: build + +deps: +ifeq ($(UNAME_S),Darwin) + @command -v brew >/dev/null 2>&1 || { echo "Homebrew kell ehhez. Telepitsd: https://brew.sh"; exit 1; } + brew install acme vice +else + @echo "A 'deps' target csak macOS-en (Darwin) automatikus. Detektalt OS: $(UNAME_S)" + @echo "Telepitsd kezzel: acme assembler + VICE emulator (xplus4)." + @exit 1 +endif + +build: $(TARGET) + +$(TARGET): $(SRCS) + $(ASM) -f cbm -o $(TARGET) $(SRC) + +run: build + $(EMU) $(TARGET) + +clear: + rm -f $(TARGET) $(PROJECT)-*.prg $(PROJECT)-*.metadata.json $(VERSION_FILE) + +rebuild: clear build + +rebuildrun: clear build run + +.PHONY: all deps build run clear rebuild rebuildrun diff --git a/example-metadata.json b/example-metadata.json new file mode 100644 index 0000000..347ce5b --- /dev/null +++ b/example-metadata.json @@ -0,0 +1,10 @@ +{ + "name": "example", + "title": "Example Plus/4 Game", + "author": "Teletype Games", + "desc": "Short description of the game.", + "site": "https://git.teletypegames.org/games/example", + "repo": "https://git.teletypegames.org/games/example", + "license": "MIT", + "version": "1.0.0" +} diff --git a/example-tasks.json b/example-tasks.json new file mode 100644 index 0000000..9de1d66 --- /dev/null +++ b/example-tasks.json @@ -0,0 +1,45 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "deps", + "type": "shell", + "command": "make deps", + "problemMatcher": [] + }, + { + "label": "build", + "type": "shell", + "command": "make build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "run", + "type": "shell", + "command": "make run", + "problemMatcher": [] + }, + { + "label": "clear", + "type": "shell", + "command": "make clear", + "problemMatcher": [] + }, + { + "label": "rebuild", + "type": "shell", + "command": "make rebuild", + "problemMatcher": [] + }, + { + "label": "rebuildrun", + "type": "shell", + "command": "make rebuildrun", + "problemMatcher": [] + } + ] +} diff --git a/example-woodpecker.yaml b/example-woodpecker.yaml new file mode 100644 index 0000000..6f29550 --- /dev/null +++ b/example-woodpecker.yaml @@ -0,0 +1,5 @@ +# One-line marker: the full pipeline is served by the update server's +# Woodpecker configuration extension. Preview it with: +# curl "https://teletypegames.org/build/config?platform=plus4" +platform: plus4 +# name: mygame # only when the software name differs from the repo name