EWM is an emulator for the machines Steve Wozniak built: the Apple 1, the Replica 1 and the Apple ][+ — plus the Enhanced Apple //e. It started life many years ago as a tiny 6502 emulator written between christmas and new year, and has since grown into a full emulator with Disk II support, graphics, and sound.
Note: EWM is a hobby project and still under development. Things may be incomplete, quirky, or broken — bug reports and pull requests are welcome. See REWRITE.md for the project's verification gates and a list of known quirks and deliberate divergences.
![]() |
![]() |
| Replica 1 — KRUSADER 1.3 from the Woz Monitor | Apple ][+ — booting the DOS 3.3 sample programs disk |
![]() |
![]() |
| Frogger in color hi-res graphics | Bandits by Sirius Software |
- Apple 1 — 6502, 8KB RAM, Woz Monitor
- Replica 1 — 65C02, 32KB RAM, KRUSADER assembler ROM
- Apple ][+ — 6502, 48KB RAM, Apple Language Card, Disk II with two
drives, a slot 7 hard drive for 32MB ProDOS block images (boots
Total Replay!), 40-column text,
low-resolution and high-resolution graphics on a green, amber or white
monochrome monitor or in color (
--color [green|amber|white|rgb]) with an optional scanline effect (--scanlines [off|light|heavy]), both switchable at runtime from the command palette, speaker sound, joystick paddles and buttons (game controllers hot-plug — Bluetooth pads connect any time — and the command palette picks between several) - Disk images —
.dsk/.do/.posector images,.nibnibble images, bit-accurate WOZ 1.0 images with copy-protection support (E7, RWTS18, half-tracks, MC3470 weak bits — seenotes/WOZ1.mdfor the compatibility table), and.2mgimages of 400K/800K 3.5" disks in the UniDisk 3.5 Controller ("Liron",{"card": "liron"}), a SmartPort card ProDOS boots from - Apple //e (Enhanced) — 65C02, 128KB main + auxiliary RAM, the built-in
language card and MMU/IOU soft switches, a swappable auxiliary slot
(
--aux): the Extended 80-Column Text Card (64K, default), the plain 80-Column Text Card (1K), or an Applied Engineering RamWorks III with up to 8MB (--aux ramworksiii:1m), 40- and 80-column text with lower case and MouseText, lo-res / hi-res / double-lo-res / double-hi-res graphics, and the //e keyboard (Open/Solid-Apple keys). Reuses the Disk II, hard drive, clock and sound. Start it withtwo --model 2e.
- A Rust toolchain (the pinned version is in
rust-toolchain.toml; rustup picks it up automatically) - SDL2 —
brew install sdl2on macOS,apt install libsdl2-devon Debian/Ubuntu
cargo build --release
Running EWM with no arguments opens the bootloader, a menu where keys 1/2/3 select the machine to start:
cargo run --release
Or start a machine directly:
# Apple ][+ with color graphics and the DOS 3.3 sample programs disk
cargo run --release -- two --color --set machine:slots:6:drive1=disks/DOS33-SamplePrograms.dsk
# Apple ][+ booting Total Replay from a ProDOS hard drive image
cargo run --release -- two --color --set 'machine:slots:7={"card":"harddrive","image":"disks/Total Replay v6.0.1.hdv"}'
# A copy-protected WOZ 1.0 image (bit-accurate Disk II emulation)
cargo run --release -- two --color --set "machine:slots:6:drive1=disks/woz/WOZ 1.0/Commando - Disk 1, Side A.woz"
# Enhanced Apple //e booting DOS 3.3 (try PR#3 for 80-column lower case)
cargo run --release -- two --model 2e --color --set machine:slots:6:drive1=disks/DOS33-SystemMaster.dsk
# Enhanced Apple //e with an 8MB RamWorks III in the auxiliary slot
cargo run --release -- two --model 2e --aux ramworksiii --set machine:slots:6:drive1=disks/DOS33-SystemMaster.dsk
# Replica 1 (Woz Monitor + KRUSADER)
cargo run --release -- one --model replica1
# Classic Apple 1
cargo run --release -- one --model apple1
--set <key>=<value> overrides one value in the machine configuration
by its colon-separated key path — any key the JSON config (below)
accepts, so --set display:monitor=amber or --set cpu:speed=3.58mhz
work the same way. Values are JSON when they parse as JSON (numbers,
booleans, whole objects like the hard-drive example above) and plain
strings otherwise.
A whole machine can also be described in a JSON file and started with
--config; files and --set overrides layer left-to-right, and the
remaining convenience flags override the result:
cargo run --release -- two --config myiie.json
{
"$schema": "https://raw.githubusercontent.com/st3fan/ewm/main/schema/ewm-config.schema.json",
"machine": {
"model": "2e",
"aux": { "card": "ramworksiii", "size": "1m" },
"slots": {
"1": { "card": "thunderclock" },
"5": { "card": "diskii", "drive1": "disks/work.dsk" },
"6": { "card": "diskii", "drive1": "disks/DOS33-SystemMaster.dsk" },
"7": { "card": "harddrive", "image": "disks/Total Replay v6.0.1.hdv" }
}
},
"display": { "monitor": "green", "scanlines": "light" },
"cpu": { "speed": "normal" }
}Ready-made configs for the classic machines live in configs/ — an
Apple ][+ with a green monitor (plus.json) and an Enhanced //e with
the extended 80-column card, a UniDisk 3.5 controller in slot 5, and a
color monitor (enhanced.json).
Neither names a disk, so pair them with a --set override, which
merges into the config's slot 6 card:
cargo run --release -- two --config configs/enhanced.json --set machine:slots:6:drive1=disks/DOS33-SystemMaster.dsk
The machine's physical layout lives in slots: any card in any slot,
up to three Disk ][ controllers (six drives), multiple hard drives,
empty slots — the Autostart scan boots the highest populated slot, as
on hardware. On the ][+, slot 0 is the memory-expansion socket: the
default machine has a Language Card (the classic 64K build), and
--set machine:slots:0:card=saturn128 swaps it for a Saturn Systems
128K RAM Board — eight Language-Card-compatible 16K banks. An explicit
slots table is taken literally, so leave out "0" — or pass
--set machine:slots:0:card=empty — for a stock 48K machine. Relative paths resolve against the config file's
directory, so a config travels with its disks. The committed JSON
Schema (schema/ewm-config.schema.json) gives editors validation and
autocomplete via the $schema key; notes/JSON_CONFIG.md has the full
plan.
--wozbug starts WozBug — a minimal, Woz-Monitor-dialect debugger — as a
line server on a local TCP port (default 6502, naturally). --break
arms breakpoints at boot, by hex address or built-in symbol (RWTS, MLI,
COUT, …), and implies the server:
cargo run --release -- two --break RWTS \
--set machine:slots:6:drive1=disks/DOS33-SystemMaster.dsk
# in another terminal:
nc localhost 6502
When a breakpoint hits, the machine freezes and the client sees the
registers. 280.29F dumps memory (a bare Return continues), 300:A9 20
deposits, R/PC=BD00 show and set registers, S single-steps, G
resumes, and DSK/SW/TEXT/SLOTS dump the disk controllers, soft
switches, text screen, and slot table. ? lists everything; the plan
lives in notes/DEBUGGING_TOOLS.md.
Each subcommand accepts --help for all options. Useful keys while the
emulator runs:
| Key | Action |
|---|---|
| Cmd-R | Reset the machine |
| Cmd-Return | Toggle fullscreen |
| Cmd-P | Pause (Apple ][+ / //e) |
| Cmd-I | Toggle the status bar with drive lights (Apple ][+ / //e) |
There are also headless terminal consoles, handy for quick experiments without a window:
cargo run -p ewm --example one # Woz Monitor
cargo run -p ewm --example two -- disks/DOS33-SystemMaster.dsk # AppleSoft / DOS 3.3
scripts/make-app.sh assembles a self-contained, double-clickable
dist/EWM.app — SDL3 is compiled in statically (CMake required at build
time), the icon is ][ rendered by the emulator's own character generator,
and the bundle is ad-hoc signed for local use. Opening the app boots the
bootloader menu; opening a disk image with it (or dragging one onto the
window) boots the ][+ with that disk — dropping a floppy on a running
machine swaps drive 1. See notes/MAC_APP.md for the plan (signing and
notarization for distribution are Phase 2).
cargo test
This runs the full verification suite: the Klaus Dormann 6502 and 65C02
functional tests, golden instruction traces, headless machine boot tests,
a complete DOS 3.3 boot with CATALOG, and a golden screenshot comparison.
The cc65 assembly sources under tests/ are manual test programs for the
machines themselves and are not part of the automated suite.
EWM was originally written in C and was ported to Rust in 2026 — the full phase-by-phase plan, parity checklist, and benchmark numbers are preserved in REWRITE.md. The original C implementation lives in the git history.
MIT, as declared in Cargo.toml (the same license the original C carried
in its source headers).


![Apple ][+ booting DOS 3.3](/st3fan/ewm/raw/main/screenshots/Screenshot%202026-07-05%20at%2011.30.59.png)

