---
title: 'Commodore Plus/4'
url: 'https://wiki.teletypegames.org/platforms/plus4'
markdown: 'https://wiki.teletypegames.org/platforms/plus4.md'
lang: en
date: '2026-09-07'
description: 'The Commodore Plus/4 is the 264-series 8-bit home computer: a 7501/8501 CPU with the 6502 instruction set, the TED chip for video and sound, 64 KB of RAM. Our Plus/4 projects are 6502 assembly, assembled by ACME into a .prg file and tested in the VICE emulator (xplus4) — the same toolchain as the C…'
taxonomy:
  tag:
    - howto
---

# Commodore Plus/4

The Commodore Plus/4 is the 264-series 8-bit home computer: a 7501/8501 CPU with the 6502 instruction set, the TED chip for video and sound, 64 KB of RAM. Our Plus/4 projects are 6502 assembly, assembled by **ACME** into a `.prg` file and tested in the **VICE** emulator (`xplus4`) — the same toolchain as the [Commodore 64](https://wiki.teletypegames.org/platforms/c64), only the machine differs.

**ACME:** https://sourceforge.net/projects/acme-crossass/ **VICE:** https://vice-emu.sourceforge.io **Tools repository**: https://git.teletypegames.org/build/plus4-build **Projects built with this toolchain:** [plus4-demo](https://git.teletypegames.org/demos/plus4-demo)

---

# What differs from the C64

|  | C64 | Plus/4 |
|---|---|---|
| BASIC start / `* =` origin | `$0801` | `$1001` |
| Entry point after a minimal SYS stub | `$0810` (`SYS 2064`) | `$100D` (`SYS 4109`) |
| Screen RAM | `$0400` | `$0C00` |
| Colour RAM | `$D800` (hue only) | `$0800` (`luminance << 4 \\| hue`, bit 7 flashes) |
| Video chip | VIC-II (`$D000`) | TED (`$FF00`–`$FF3F`): `$FF15` background, `$FF19` border, `$FF1D`/`$FF1C` raster line |
| Sound | SID | TED, two voices |
| Keyboard | CIA1 (`$DC00`/`$DC01`) | `$FD30` column select + `$FF08` row latch |
| Emulator | `x64sc` | `xplus4` |

The KERNAL jump table is compatible (`$FFD2` CHROUT, `$FFE4` GETIN), so the exit path of a program looks the same on both machines. ACME needs no special mode: `!cpu 6502` covers the 7501/8501.

One ACME habit worth knowing for both machines: `!scr` maps *lowercase* ASCII to the uppercase screen codes of the default character set. Write `!scr "teletype games"` to get `TELETYPE GAMES` on screen; uppercase source text comes out as graphics symbols.

---

# Makefile

All `.asm` files live in the project root. `main.asm` is the single entry point and `!src`-includes the rest.

```text
project-root/
├── main.asm          # entry point, includes the others
├── metadata.json     # catalog metadata + version
├── Makefile
└── README.md
```

## Variables

| Variable | Description | Default |
|---|---|---|
| `PROJECT` | Name used in artifacts and the catalog | — |
| `ASM` | Assembler binary | `acme` |
| `EMU` | Emulator binary | `xplus4` |
| `SRC` | Entry point | `main.asm` |
| `TARGET` | Output program | `$(PROJECT).prg` |
| `METADATA` | Metadata file | `metadata.json` |

## metadata.json

```json
{
  "name": "example",
  "title": "Example Plus/4 Game",
  "author": "Teletype Games",
  "desc": "Short description of the game.",
  "site": "https://git.teletypegames.org/games/example",
  "license": "MIT",
  "version": "1.0.0"
}
```

`name` **must match** `PROJECT`. `version` drives the pipeline's version step. The rest goes into the game catalog.

## Targets

```sh
make deps        # brew install acme vice (macOS only)
make build       # assemble main.asm -> <project>.prg, only when an .asm changed
make run         # build, then launch in VICE (xplus4)
make clear       # remove the .prg and CI artifacts
make rebuild     # clear + build
make rebuildrun  # clear + build + run
```

## Headless smoke test

VICE can run a build for a few emulated seconds and leave a screenshot behind, which is enough to see that a title screen comes up:

```sh
xplus4 +sound -warp -limitcycles 30000000 -exitscreenshot shot.png -autostartprgmode 1 <project>.prg
```

## VS Code

Copy `example-tasks.json` to `.vscode/tasks.json`. It wraps the same targets, with `build` as the default build task (`Cmd+Shift+B`).

## Generated artifacts

| File | Description |
|---|---|
| `<project>.prg` | The assembled program |
| `<project>-<version>.prg` | Versioned release copy (CI) |
| `<project>-<version>.metadata.json` | Versioned metadata (CI) |

## Requirements

Local only: `make`, `acme`, and `xplus4` from VICE for `run`.

Example Makefile: https://git.teletypegames.org/build/plus4-build/src/branch/master/example-makefile.make

---

# Pipeline

The pipeline is not in the game repo. Woodpecker asks the update server, and WarpEngine serves the whole config from `/build/config` (template `lib/warp_engine/ci/woodpecker/platforms/plus4/pipeline.yaml.erb`). WarpEngine knows the platform as `plus4` since 0.10.1.

The repo needs three things:

```yaml
# .woodpecker.yaml
platform: plus4
name: mygame   # only when the software name differs from the repo name
```

- `metadata.json`
- the `application_token` Woodpecker secret: a WarpEngine ApplicationToken with `update` + `upload` scopes

The builder and the player image come from the update server's CI adapter config (`platforms:` in the WarpEngine initializer). Preview the served pipeline:

```sh
curl "https://teletypegames.org/build/config?platform=plus4"
```

## Steps

| Step | What it does |
|---|---|
| 1. version | the engine's shared step (since WarpEngine 1.7): the version from `metadata.json`; on a tag a version tag has to match it; outside the repo's default branch and on a `dev-<branch>` tag `dev-<version>-<branch>`, with no version `dev-<commit>`; write `.version` |
| 2. build | assemble with ACME in the `plus4-builder` image, producing `<project>-<version>.prg` and `<project>-<version>.metadata.json` |
| 3. package | **on the tag only:** `commodore-pack plus4` in the `commodore-player` image: `<project>-<version>.html.zip` (EmulatorJS page with the self-hosted `vice_xplus4` core), plus `-linux-x64.zip`, `-win-x64.zip` and `-mac-arm64.zip` (yapeSDL with a launcher that sets the machine type and autostarts the `.prg`) |
| 4. upload | **on the tag only:** `POST /build/upload` for the `.prg`, the metadata and the players that were built (all six files when packaging succeeded), `X-Update-Secret` header |
| 5. publish | **on the tag only:** `POST /build/publish?platform=plus4` |
| 6. tag | the engine's shared step (since WarpEngine 1.9), **after a push only**: a new version in the source gets its `v<version>` tag, whose pipeline runs the steps marked *on the tag only*; a version tagged already releases nothing; a dev build moves the branch's `dev-<branch>` tag onto the commit, so the newest dev build is published. See *Versions and releases* on the [WarpEngine](https://wiki.teletypegames.org/engines/warp-engine) page |

Raising the version is the release: a push builds and tags, and the tag's pipeline uploads and publishes (WarpEngine 1.9). A push that leaves the version as it is only builds.

## Result

The server reads the uploaded metadata, creates or updates the catalog entry, and registers five assets on the release: the `.prg` as the downloadable cartridge, the EmulatorJS page as the web-playable build (the catalog's "Play in browser" button, served from `/file/<name>-<version>/`), and the three yapeSDL bundles as the Linux, Windows and macOS downloads. Since WarpEngine 0.11 the two Commodore platforms expect the same asset kinds; earlier releases carry only the cartridge until they are rebuilt.

Since WarpEngine 1.7 the `.prg` of every public Plus/4 title's latest release is also on the Plus/4 shelf, under a name without a version and with an `INDEX.TXT`: `ftp://ftp.teletypegames.org/PLUS4` and `http://archive.teletypegames.org/PLUS4`.

## Demo project

[plus4-demo](https://git.teletypegames.org/demos/plus4-demo) — the Teletype Games title card: a gold colour wash across the title driven by the TED raster beam, a blinking prompt, and a clean return to BASIC on any key. The Plus/4 sibling of the [C64 Demo](https://wiki.teletypegames.org/projects/c64-demo).

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [Platforms](https://wiki.teletypegames.org/platforms.md)
- Previous: [Phaser](https://wiki.teletypegames.org/platforms/phaser.md)
- Next: [TIC-80](https://wiki.teletypegames.org/platforms/tic80.md)
