diff --git a/.gitignore b/.gitignore
index 9ba5b87..869291d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ opencode-config/package-lock.json
internal/preproc/lib/
.bun/
*.asm
+.config/
diff --git a/BUILDNUM b/BUILDNUM
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/BUILDNUM
@@ -0,0 +1 @@
+1
diff --git a/Dockerfile b/Dockerfile
index 2f29229..0640b5e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,5 @@
FROM ghcr.io/anomalyco/opencode:1.14.48
+#FROM ghcr.io/anomalyco/opencode:latest
RUN apk add --no-cache go gcc musl-dev
diff --git a/README.md b/README.md
index cda2058..97ef35b 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,35 @@
A high-level 6502 cross-compiler targeting the ACME Cross-Assembler. c65gm provides a more expressive language for writing 6502 assembly programs, with features like functions, type-checked variables, control flow structures, and compile-time optimizations.
+## Download
+
+Pre-built binaries are available from the [Releases page](https://github.com/YOUR_USERNAME/c65gm/releases). Each release follows the naming pattern `c65gm_v{MAJOR}.{BUILD}_os_arch`:
+
+| File | Platform |
+|------|----------|
+| `c65gm_v1.1_linux_amd64.tar.gz` | Linux (x86_64) |
+| `c65gm_v1.1_linux_arm64.tar.gz` | Linux (ARM64) |
+| `c65gm_v1.1_darwin_amd64.tar.gz` | macOS (Intel) |
+| `c65gm_v1.1_darwin_arm64.tar.gz` | macOS (Apple Silicon) |
+| `c65gm_v1.1_windows_amd64.zip` | Windows (x86_64) |
+
+Each release includes a `checksums.txt` file for verifying download integrity:
+
+```bash
+sha256sum -c checksums.txt
+```
+
+### Verify with the annotated tag
+
+Every release is created from an annotated git tag containing the Go compiler version and SHA256 checksums. To inspect:
+
+```bash
+git fetch --tags
+git tag -l v1.1
+```
+
+The tag message shows the exact checksums. You can reproduce the build from the tag and verify your binary matches:
+
## What It Does
c65gm compiles high-level source code into ACME assembler syntax for the 6502 processor (Commodore 64 and similar platforms). It provides:
@@ -248,6 +277,68 @@ The example directories also contain `cm.sh` scripts showing the old build metho
- Kate: copy XML to ~/.local/share/org.kde.syntax-highlighting/syntax/
- Sublime: copy .sublime-syntax to Packages/User/
+## Release Process
+
+This section documents how to create a new release for maintainers.
+
+### Prerequisites
+
+- Go 1.25.1+ installed
+- `sha256sum` (Linux) or `shasum` (macOS)
+- Git with write access to the repository
+
+### Steps
+
+The release script does everything automatically. Just run it:
+
+```bash
+bash scripts/release.sh
+```
+
+This performs the following sequence:
+
+1. **Verify git clean** — fails if uncommitted changes exist
+2. **Read BUILDNUM** — e.g., `42` → version `1.42`
+3. **Verify tag available** — fails if tag `v1.42` already exists
+4. **Bump BUILDNUM** — writes `43` to `BUILDNUM`
+5. **Build all targets** — 5 platform binaries with version injected via ldflags
+6. **Generate SHA256 checksums** — written to `dist/checksums.txt`
+7. **Commit BUILDNUM** — `git commit` with checksums in the commit body
+8. **Create annotated tag** — `git tag -a v1.42` pointing at the pre-bump commit, with checksums and Go version in the tag message
+
+### Publish
+
+```bash
+git push --follow-tags origin
+```
+
+This pushes both the new commit and the annotated tag. The tag is the source of truth for verification.
+
+### Verify a release
+
+```bash
+git checkout v1.42
+# You now have the exact source (BUILDNUM=42)
+go build -trimpath -ldflags="-s -w -X main.version=1.42"
+sha256sum c65gm
+# Compare with the checksum in: git tag -l v1.42
+```
+
+### Reproducible builds
+
+The release script uses these flags for deterministic builds:
+
+```bash
+go build -trimpath -ldflags="-s -w -X main.version=${VERSION}"
+```
+
+- `-trimpath` strips local filesystem paths
+- `-s -w` strips symbol table and DWARF debug info
+- `-X main.version` injects the version
+- `CGO_ENABLED=0` ensures fully static binaries
+
+Binaries built from the same source with the same Go version produce identical hashes. The tag message captures the Go version and expected checksums for verification.
+
## License
Copyright (C) 1999, 2025 Mattias Hansson
diff --git a/main.go b/main.go
index 87a4c1d..bfb58bb 100644
--- a/main.go
+++ b/main.go
@@ -20,6 +20,8 @@ import (
// Copyright (C) 1999, 2025 Mattias Hansson
// Distributed under GPL.
+var version = "dev"
+
// ANSI color codes for error messages
const (
colorRed = "\033[31m"
@@ -28,8 +30,14 @@ const (
)
func main() {
+ // Check for version flag before banner
+ if len(os.Args) == 2 && (os.Args[1] == "--version" || os.Args[1] == "-V") {
+ fmt.Printf("c65gm v%s\n", version)
+ return
+ }
+
fmt.Println("c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler.")
- fmt.Println("Copyright (C) 1999, 2025 Mattias Hansson. v1.0.0")
+ fmt.Printf("Copyright (C) 1999, 2025 Mattias Hansson. v%s\n", version)
fmt.Println("Distributed under GPL.")
fmt.Println()
@@ -222,6 +230,7 @@ func printUsage() {
fmt.Println(" -o, --output Output file (default: .prg for build, .asm for compile)")
fmt.Println(" --keep-asm Keep intermediate assembly file (build command only)")
fmt.Println(" --no-cbm Don't add -f cbm flag to ACME (build command only)")
+ fmt.Println(" -V, --version Show version")
}
func runBuildCommand(args []string) {
diff --git a/opencode-config/opencode.json b/opencode-config/opencode.json
index 86da758..cc67ed9 100644
--- a/opencode-config/opencode.json
+++ b/opencode-config/opencode.json
@@ -2,12 +2,16 @@
"$schema": "https://opencode.ai/config.json",
"agent": {
"plan": {
- "model": "deepseek/deepseek-reasoner",
- "description": "Planning and architecture analysis using DeepSeek Reasoner"
+ "model": "deepseek/deepseek-v4-pro",
+ "options": {
+ "thinking": { "type": "enabled" },
+ "reasoningEffort": "high"
+ },
+ "description": "Planning and architecture analysis using DeepSeek V4 Pro"
},
"build": {
- "model": "deepseek/deepseek-chat",
- "description": "Implementation and coding using DeepSeek Chat"
+ "model": "deepseek/deepseek-v4-flash",
+ "description": "Implementation and coding using DeepSeek V4 Flash"
}
}
}
\ No newline at end of file
diff --git a/scripts/release.sh b/scripts/release.sh
new file mode 100755
index 0000000..cda0f0f
--- /dev/null
+++ b/scripts/release.sh
@@ -0,0 +1,238 @@
+#!/bin/bash
+set -euo pipefail
+
+# c65gm Release Builder
+#
+# Fully automated release process:
+# 1. Verifies git working tree is clean
+# 2. Reads BUILDNUM, derives version (MAJOR.BUILDNUM)
+# 3. Verifies the tag doesn't already exist
+# 4. Bumps BUILDNUM
+# 5. Builds all target binaries (embedded lib, version injected)
+# 6. Generates SHA256 checksums
+# 7. Commits the BUILDNUM bump (with checksums in commit body)
+# 8. Creates an annotated tag pointing at PRE-bump commit
+# (tag message contains checksums + build info for verification)
+#
+# To publish the release:
+# git push --follow-tags origin
+
+MAJOR=1
+
+print_ok() { echo " [OK] $1"; }
+print_err() { echo " [ERROR] $1" >&2; }
+
+echo ""
+echo "=== c65gm Release Builder ==="
+echo ""
+
+# ---------------------------------------------------------------------------
+# 1. Verify git working tree is clean
+# ---------------------------------------------------------------------------
+if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
+ echo ""
+ git status --short
+ echo ""
+ print_err "working tree is not clean — commit or stash changes first"
+ exit 1
+fi
+print_ok "Working tree is clean"
+
+# ---------------------------------------------------------------------------
+# 2. Read BUILDNUM
+# ---------------------------------------------------------------------------
+if [ ! -f BUILDNUM ]; then
+ echo "1" > BUILDNUM
+fi
+
+BUILDNUM=$(cat BUILDNUM)
+VERSION="${MAJOR}.${BUILDNUM}"
+TAG="v${VERSION}"
+
+# ---------------------------------------------------------------------------
+# 3. Verify tag doesn't already exist
+# ---------------------------------------------------------------------------
+if git rev-parse --quiet --verify "refs/tags/${TAG}" >/dev/null 2>&1; then
+ print_err "tag ${TAG} already exists — bump BUILDNUM manually if needed"
+ exit 1
+fi
+print_ok "Tag ${TAG} is available"
+
+# ---------------------------------------------------------------------------
+# 4. Save source commit (before any changes)
+# ---------------------------------------------------------------------------
+SOURCE_COMMIT=$(git rev-parse HEAD)
+if [ -z "$SOURCE_COMMIT" ]; then
+ print_err "could not determine HEAD commit"
+ exit 1
+fi
+
+echo ""
+echo " Version: ${TAG}"
+echo " Source: ${SOURCE_COMMIT}"
+echo " BUILDNUM: ${BUILDNUM} (bumps to $((BUILDNUM + 1)))"
+echo ""
+
+# ---------------------------------------------------------------------------
+# 5. Bump BUILDNUM
+# ---------------------------------------------------------------------------
+NEXT_BUILDNUM=$((BUILDNUM + 1))
+echo "${NEXT_BUILDNUM}" > BUILDNUM
+
+# ---------------------------------------------------------------------------
+# 6. Copy lib for embedding
+# ---------------------------------------------------------------------------
+echo " Preparing embedded library..."
+rm -rf internal/preproc/lib
+cp -r lib internal/preproc/
+print_ok "Library copied for embedding"
+
+# ---------------------------------------------------------------------------
+# Build metadata
+# ---------------------------------------------------------------------------
+GO_VERSION="$(go version 2>/dev/null || echo 'unknown')"
+BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u +%Y-%m-%d)"
+
+LDFLAGS="-s -w -X main.version=${VERSION}"
+DIST="dist"
+mkdir -p "$DIST"
+
+# SHA256 command detection (Linux vs macOS)
+SHA_CMD=""
+if command -v sha256sum &>/dev/null; then
+ SHA_CMD="sha256sum"
+elif command -v shasum &>/dev/null; then
+ SHA_CMD="shasum -a 256"
+else
+ print_err "no sha256sum or shasum command found"
+ exit 1
+fi
+
+# Targets
+TARGETS=(
+ "linux/amd64"
+ "linux/arm64"
+ "darwin/amd64"
+ "darwin/arm64"
+ "windows/amd64"
+)
+
+echo ""
+echo " Building targets: ${TARGETS[*]}"
+echo ""
+
+# ---------------------------------------------------------------------------
+# 7. Build all targets
+# ---------------------------------------------------------------------------
+BUILD_DIR="$(mktemp -d)"
+trap 'rm -rf "$BUILD_DIR"' EXIT
+
+for target in "${TARGETS[@]}"; do
+ GOOS="${target%%/*}"
+ GOARCH="${target##*/}"
+
+ bin_name="c65gm"
+ [ "$GOOS" = "windows" ] && bin_name="c65gm.exe"
+
+ echo " ${target}..."
+
+ GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=0 \
+ go build -trimpath -ldflags="$LDFLAGS" -o "${BUILD_DIR}/${bin_name}"
+
+ archive_name="c65gm_v${VERSION}_${GOOS}_${GOARCH}"
+ if [ "$GOOS" = "windows" ]; then
+ (cd "$BUILD_DIR" && zip -q "${OLDPWD}/${DIST}/${archive_name}.zip" "$bin_name")
+ echo " => ${DIST}/${archive_name}.zip"
+ else
+ tar -czf "${DIST}/${archive_name}.tar.gz" -C "$BUILD_DIR" "$bin_name"
+ echo " => ${DIST}/${archive_name}.tar.gz"
+ fi
+
+ rm -f "${BUILD_DIR}/${bin_name}"
+done
+
+echo ""
+print_ok "All targets built"
+
+# ---------------------------------------------------------------------------
+# 8. Generate checksums
+# ---------------------------------------------------------------------------
+echo ""
+echo "=== SHA256 checksums ==="
+echo ""
+(
+ cd "$DIST"
+ $SHA_CMD * > checksums.txt
+ cat checksums.txt
+)
+
+CHECKSUMS_TEXT="$(cat "${DIST}/checksums.txt")"
+print_ok "Checksums generated"
+
+# ---------------------------------------------------------------------------
+# 9. Commit BUILDNUM bump
+# ---------------------------------------------------------------------------
+echo ""
+echo "=== Creating commit ==="
+
+COMMIT_MSG=$(cat << COMMIT_EOF
+${TAG}
+
+Built with: ${GO_VERSION}
+Build date: ${BUILD_DATE}
+
+SHA256 checksums:
+${CHECKSUMS_TEXT}
+COMMIT_EOF
+)
+
+git add BUILDNUM
+git commit -m "$COMMIT_MSG"
+print_ok "Commit created (BUILDNUM bumped to ${NEXT_BUILDNUM})"
+
+# ---------------------------------------------------------------------------
+# 10. Create annotated tag pointing at source commit
+# ---------------------------------------------------------------------------
+echo ""
+echo "=== Creating tag ==="
+
+TAG_MSG=$(cat << TAG_EOF
+c65gm ${TAG}
+
+Built with: ${GO_VERSION}
+Build date: ${BUILD_DATE}
+
+SHA256 checksums:
+${CHECKSUMS_TEXT}
+
+To reproduce this build:
+ git checkout ${TAG}
+ go build -trimpath -ldflags="${LDFLAGS}"
+
+Then verify:
+ sha256sum c65gm
+TAG_EOF
+)
+
+git tag -a "$TAG" "$SOURCE_COMMIT" -m "$TAG_MSG"
+print_ok "Tag ${TAG} created (pointing at ${SOURCE_COMMIT})"
+
+# ---------------------------------------------------------------------------
+# 11. Summary
+# ---------------------------------------------------------------------------
+echo ""
+echo "============================================"
+echo " c65gm ${TAG} released!"
+echo ""
+echo " Source commit: ${SOURCE_COMMIT}"
+echo " BUILDNUM: ${BUILDNUM} → ${NEXT_BUILDNUM}"
+echo " Artifacts in: ${DIST}/"
+echo " Go version: ${GO_VERSION}"
+echo ""
+echo " To publish:"
+echo " git push --follow-tags origin"
+echo ""
+echo " Artifacts:"
+ls -lh "${DIST}/" | sed 's/^/ /'
+echo "============================================"
+echo ""