Skip to content

chrisgleissner/c64forge

Repository files navigation

c64forge

c64forge logo

A workspace for building, patching, and deploying firmware for the 1541ultimate and Spiffy_Ultimate lineages.

License: GPL v3 Platform Targets

One ./forge command drives the whole development cycle:

  • reproducible setup
  • convenient build
  • separation of local and upstream changes using an overlay file system
  • transient and persistent deployment
  • device recovery

Supported build targets: c64u, u64, ue2, u2.

Workspace Model

Three source layers, one editable view:

  • upstream/ — pristine checkouts pinned by SHA in manifest.yaml
  • patches/ — tracked local deltas, shareable and upstreamable
  • patches-private/ — ignored local-only overlays
  • work/ — merged overlay view; this is what you edit

Keeping upstream and patches on separate layers means the editor still sees one normal source tree, but you can export clean patches for upstream without untangling them from private tinkering.

Build state lives in .local/, output in out/; both are disposable.

Rules:

  • edit under work/1541ultimate or work/spiffy-ultimate, never upstream/
  • if overlays are unavailable, update the matching file under patches/<repo>/

Quick Start

git clone --recurse-submodules <repo-url> c64forge
cd c64forge
./forge setup            # bootstraps .local/, work/, out/, mounts overlays
./forge status           # sanity check
./forge build c64u       # or u64, ue2, u2
./forge run c64u         # transient hardware test

If you cloned without submodules: git submodule update --init --recursive. Skip host-dep installs with ./forge setup --skip-host-deps.

A healthy status looks like:

Workspace root: /home/user/dev/c64/c64forge
Local config:   present (default_target=u64)
Host deps:      ready
Docker:         ready
Overlay:        fuse-overlayfs
U64 device:     u64 reachable
C64U device:    c64u reachable

[1541ultimate]
  sha:          8b0ad2e99f4106a3c27773a6d233047d436a8382
  lower:        fresh
  work:         mounted at .../work/1541ultimate

[spiffy-ultimate]
  sha:          3b582da78c57fb5b2d53bacb6a64dea5a4535f66
  lower:        fresh
  work:         mounted at .../work/spiffy-ultimate

Use --dry-run the first time you invoke run, stage, or flash on a given machine.

Commands

Warning

No warranty. If you load your own build, flash the wrong image, or brick a device, the repair is yours. However, only the flash command is potentially dangerous.

Command Purpose
./forge setup Bootstrap workspace, mount merged trees. --skip-host-deps supported.
./forge status Config, deps, overlay, device reachability, pinned SHAs.
./forge fix Rebuild local state and remount overlays.
./forge targets List supported artifacts, command paths, and risk levels.
./forge build [target...] Build one or more targets. Builds all top-level firmware targets when omitted.
./forge run [target] Build and deploy transiently with no flash write. u64, c64u.ue2.
./forge stage [target] Upload a full image only; no execution, no flash. u64, ue2, c64u.ue2.
./forge flash [target] Guarded app-only persistent flash helper (DANGER ZONE). c64u.ue2 only.
./forge recover [target] Upstream JTAG recovery if direct persistent flash failed. u64, ue2, c64u.ue2. --jtag-url to override.
./forge update [repo|all] [ref] Advance upstream checkout(s) to the tracked origin branch or an explicit ref, then run sync.
./forge sync [repo|all] Compare indexed patch files against the current upstream checkout(s).
./forge pr 1541ultimate <path...> Stage selected patch files in a local 1541ultimate PR branch.
./forge clean Remove disposable build state, unmount work trees.
./forge help Built-in help.

When a target is omitted, build builds all; run, stage, flash, and recover use default_target from c64forge.local.yaml.

Targets

Name Kind Source Used by Artifact Notes
u64 firmware 1541ultimate build, run, stage update_u<up3>_f<forge3>.u64 Ultimate 64 Elite
ue2 firmware 1541ultimate build, stage, recover update_u<up3>_f<forge3>.ue2 Ultimate 64 Elite II
u2 firmware 1541ultimate build update_u<up3>_f<forge3>.u2r Ultimate II (RiscV CPU)
c64u.ue2 firmware spiffy-ultimate build, stage update_u<up3>_f<forge3>.c64u.ue2 C64 Ultimate
kstart.c64u.ue2 launcher helper spiffy-ultimate build, run c64u.ue2 update_u<up3>_f<forge3>.kstart.c64u.ue2 app-only transient helper; no flash write
kburn.c64u.ue2 launcher helper spiffy-ultimate build, flash c64u.ue2 update_u<up3>_f<forge3>.kburn.c64u.ue2 app-only persistent helper; writes flash

Artifact Names

Artifacts staged in out/ use this structure:

update_u<up3>_f<forge3>.<suffix>

  • up3: first 3 hex digits of the upstream repo commit used to build the artifact
  • forge3: first 3 hex digits of the local c64forge commit that staged it
  • <suffix>: target-specific artifact suffix such as u64, ue2, u2r, c64u.ue2, kstart.c64u.ue2, or kburn.c64u.ue2

Examples:

  • update_u8b0_f4e.u64
  • update_u3b5_f4e.c64u.ue2
  • update_u3b5_f4e.kstart.c64u.ue2

Full C64U image flashing is the highest-risk update path in this repo. ./forge does not remote-flash the full c64u.ue2 image directly; instead, ./forge stage c64u.ue2 uploads it, and a later device-side full-image update performs the dangerous persistent change.

Configuration

Local settings live in c64forge.local.yaml, created from c64forge.local.yaml.example on first setup:

default_target: u64
u64_host: u64
ue2_host: ue2
ue2_install_path: /USB1/Firmware/custom/
c64u_host: c64u
c64u_ftp_path: /Temp/
c64u_telnet_port: 23
build_tools_dir: ""
jtag_url: ftdi://ftdi:232h/0
Field Role
default_target target used when run/stage/flash/recover is called without one
u64_host Ultimate 64 network host
ue2_host Ultimate 64 Elite II network host
ue2_install_path firmware upload path for the plain ue2 image
c64u_host Commodore 64 Ultimate network host
c64u_ftp_path temporary upload path for C64U flows
c64u_telnet_port telnet port used to trigger temporary launches
build_tools_dir optional host-side build tools path
jtag_url default JTAG URL for recover

Required host tools: docker, git, rsync, ping, mountpoint, fuse-overlayfs, fusermount3, curl, telnet, python3, make.

Patch Maintenance

./forge sync 1541ultimate        # or spiffy-ultimate, or all
./forge pr 1541ultimate path/to/file1 path/to/file2

Typical workflow:

edit -> sync -> pr
edit -> update -> sync -> pr
sync conflict|orphaned -> edit -> sync

sync is index-based, not merge-based: it compares each file in patches/ against the base hash recorded in patches/index.tsv and the current file content in upstream/. clean means upstream still matches the recorded base, upstreamed means upstream now matches your local patch, and conflict or orphaned means the patch needs attention before export.

update first moves upstream/1541ultimate and/or upstream/spiffy-ultimate forward, then runs that same check. pr does not open GitHub automatically; it creates a local staging branch under .local/staging/1541ultimate for review, push, and manual PR creation.

License

c64forge itself is GPL v3 — see LICENSE.txt. Pinned upstream repositories under upstream/ retain their own notices and licenses.