Hardware-in-the-loop development platform for the Commodore 64.
ultimate-tool docs EXISTING_PROJECT.md
6.0 kB
Markdown
at main

Onboarding an existing project #

Ultimate Tool does not require you to adopt its diagnostics SDK or its template layout. An existing C64 project becomes a toolkit project by adding one c64dev.toml beside its own build. That single file then drives the same versioned build, target, run, and evidence operations that every interface shares.

The onboarding tiers, the manifest fields they require, and the verified projects each tier has carried are described in PORTABILITY.md. This page is the short path for a developer bringing in a project that already builds a PRG.

Start from the screen-observation template #

examples/existing-project/c64dev.toml is a copyable schema 11 manifest that builds the project with its own make and checks one screen. Copy it next to your build and change three things:

  • build.command: the argument array that builds the PRG.
  • build.artifact: the PRG the build produces.
  • tests.expected_screen: a bounded string that must appear on screen.
schema_version = 11

[build]
command = ["make", "build"]
working_directory = "."
artifact = "build/game.prg"
toolchain = "the project's own pinned toolchain"

build.command is an argument array, not a shell string. working_directory and artifact are project-relative, and the build runs from working_directory, so a relative program name such as ./tools/asm resolves there exactly as it would for the launch itself. A bare program name is searched on PATH.

Screen observation needs no source changes: it launches the built PRG, waits for the screen to stabilize, and compares the visible text. It does not read the mailbox. [diagnostics] remains part of the required manifest shape, so keep the conventional values unless your project already reserves that memory. A screen test rejects expected_terminal, startup_command, and heartbeat_timeout_ms, because that tier provides none of them.

Check the local prerequisites before you run anything #

Bringing up a manifest usually means discovering a missing tool or a wrong path. The read-only prerequisite diagnosis reports those without building or launching anything:

$ ultimate-tool doctor --manifest c64dev.toml --target fake

It is deliberately split from execution and reports a versioned row per local requirement:

  • manifest: the file loads and passes manifest validation. A missing or malformed file is reported as a row, not as silence.
  • build_executable: the build.command executable resolves through PATH or as an explicit path, and carries an executable bit.
  • build_working_directory: build.working_directory exists and is a directory.
  • vice_executable (only when --target vice): the configured VICE executable resolves to an existing file. Nothing about it is executed or version-probed.
  • ultimate_target (only when --target ultimate): reported as not_checked.

Build-command inputs are not_checked: the manifest does not enumerate an arbitrary command's source/dependency inputs, so doctor does not guess them or execute the command. The explicit build or first-run materializer validates those inputs later. Missing future build outputs are not missing prerequisites.

Each row is ready, missing, or not_checked. The overall report is ready when no checked local requirement is missing; not_checked remains an explicit limitation, not a verification claim. The process exits 0 when it is; otherwise it exits non-zero.

The command purposefully does none of the following: it does not build, spawn a process, inspect a container image, probe a version, contact a target, read a credential, or write under .ultimate-tool. It never runs a command from the manifest. Missing build outputs are expected — the build has not run yet — and are not reported as missing prerequisites.

The Ultimate row is not_checked because Ultimate readiness depends on network reachability and device identity, which a local read-only diagnosis cannot establish; that is the separate probe workflow, and it needs no credentials here. Finding an executable is not verifying its version, and a ready report is not evidence that the project builds, that a target was contacted, or that a program passed.

Grow the manifest only as the project needs it #

Add fields when a real capability needs them, not up front:

  • Real symbol analysis and budgets. On schema 7, declare build.symbol_sources (an LLVM lld .map or an llvm-mos .elf) plus optional build.source_remaps so resolved addresses can map back to project-relative source lines. On schema 8, add [[build.budgets]] to observe or enforce section sizes and symbol gaps. See BUILD_ANALYSIS.md.
  • Mailbox diagnostics. Adopting the Rust-MOS SDK opts into ordered events, one acknowledged startup command, heartbeat hang detection, crash classification, and bounded crash-state evidence. This tier costs memory and toolchain work; adopt it only where that cost has been measured. A screen test is never silently promoted to mailbox support.
  • Hardware. Screen-memory evidence on a C64 Ultimate requires the identity-pinned probe and the separate authorized deployment/reset path. Do not infer hardware behavior from VICE or host tests.

The unchanged-binary portability proof carried roguelike-c64 (Rust-MOS) and Polyself from nethack-6510 (llvm-mos C with overlays and a REU image) through this tier without source changes, then replayed the same artifacts on a pinned C64 Ultimate. Those outcomes are recorded in PORTABILITY.md; their private trees are not required to use the template here.