Skip to content

relmer/Casso

Repository files navigation

Casso

CI License: MIT

About

Casso is a retro / classic-machine platform emulator and from-scratch AS65-compatible 6502 assembler, written in C++. Today the platform emulator targets the Apple II family (][, ][+, //e); the abstractions are generic enough to host other 6502-based machines later.

Two of the three built-in themes booting the casso-rocks demo disk — same Apple //e core, different chrome:

Skeuomorphic Dark Modern
Casso Skeuomorphic theme booting the casso-rocks DHGR demo Casso Dark Modern theme booting the casso-rocks DHGR demo

The project includes:

  • Apple II platform emulator — GUI-based Apple II, II+, and //e emulator with D3D11 rendering, WASAPI audio, Disk II controller with realistic mechanical sounds, data-driven machine configs, 80-column text + Double Hi-Res, auxiliary RAM, audit-correct Language Card state machine, and cycle-accurate IRQ/NMI infrastructure.
  • 6502 CPU emulator — passes Klaus Dormann's functional test suite and all 151 legal-opcode sets from Tom Harte's SingleStepTests (10,000 vectors each).
  • AS65-compatible assembler — a from-scratch reimplementation of Frank A. Kingswood's AS65, intended as a drop-in replacement. Supports the complete AS65 syntax: macros, conditional assembly (if/ifdef/ifndef/else/endif), the full expression evaluator (arithmetic, bitwise, logical, shift, </> byte selectors, current-PC *), equ/= constants, include, three-segment model (code/data/bss), AS65-style listing output, and AS65 command-line flags (-l, -t, -s, -s2, -z, -c, -w, -d, -g, ...) including flag concatenation (-tlfile).
  • CLI tool — runs as an AS65-style assembler by default, or with the run subcommand to load and execute a binary or assembly source.
  • First-run asset bootstrap — Casso fetches the ROMs, sample disks, and Disk II audio samples it needs on first launch (with user consent), so a fresh Casso.exe boots to a usable //e BASIC prompt with no manual setup.
  • Headless test harnessHeadlessHost drives the emulator with no Win32 window, enabling deterministic integration tests for cold boot, disk boot, video framebuffer hashing, and reset semantics.
  • 1500+ unit tests — comprehensive coverage of CPU instruction encoding, addressing modes, arithmetic, branching, assembler features, audio pipeline (speaker + drive), //e MMU + Language Card, video timing, Disk II nibble engine, WOZ + nibblized image formats, 80-col + DHGR video, reset semantics, perf budget, and backwards-compat for ][/][+ machines.

What's New

See CHANGELOG.md for the granular history.

UI Overhaul (v1.4.1171)

Casso's entire chrome moved from the legacy Win32 menu bar / Win32 dialogs to a borderless, themed shell rendered straight onto the same D3D11 framebuffer that draws the emulator video — using a native Direct2D / DirectWrite pipeline (DxUiPainter + DwriteTextRenderer), no third-party UI engine.

Three built-in themes — Skeuomorphic, Dark Modern, Retro Terminal — hot-swappable from Settings → Theme with no restart and no machine reset. Each theme ships under Resources/Themes/<Name>/ (extracted to Themes/<Name>/ at first run) with a theme.json describing colors, CRT defaults, drive visual profile, and other UI tokens consumed by the native widget renderer. The token-based custom-theme authoring surface is still being wired through the native widgets — see docs/themes/AUTHORING.md for the current state.

Theme picker hot-swapping between Skeuomorphic, Dark Modern, and Retro Terminal

Skeuomorphic drive widgets with realistic Apple Disk II faceplates: perspective-projected case top with two indented lid panels that taper toward the back, nine vent slits down each side, beige case wrapping a black inset faceplate on all four sides, cantilever door hinged at the slot top that tilts up and back (tucking inside the case with a small flap visible when fully open) revealing a recessed finger-pull behind it, status LED, and the Cassowary rainbow logo. Click a drive to pick a disk image, or drag-and-drop a .dsk / .do / .po / .nib file onto it. Eject animates the door open even on an empty drive.

Skeuomorphic drive widgets: Drive 1 active with red IN USE LED, Drive 2 idle

Consolidated Settings panel replaces the old OptionsDialog and MachinePickerDialog. Machine selection, machine info, emulation speed, video color mode, disk write mode, floppy sound + mechanism, write-protect, theme picker, and the new CRT controls live in one non-modal in-window panel with full keyboard navigation.

Settings panel — Machine tab with machine, CPU speed, write protect, write mode, and drive audio controls

CRT effects — scanlines, phosphor bloom, and color bleed (each independently toggleable, with its own parameter sliders), plus persistence trails, contrast, and gamma sliders. Per-monitor presets (Color / Green / Amber / White) seed sensible defaults; themes can override; user tweaks persist as overrides on top of either. The Settings popup gets out of your way as you scrub a control — the panel fades, the emulator behind it stays sharp inside a per-pixel clip, and only the focused control remains opaque — so you can evaluate the effect of every parameter change live.

Display tab CRT controls — monitor preset, brightness, contrast, gamma, scanlines, bloom, color bleed, persistence

Live-preview mode — Settings panel fades while the focused Intensity slider stays sharp over the live emulator output

Unified user preferences persist in %LOCALAPPDATA%\Casso\UserPrefs.json: global UI state under global, and per-machine deltas under machines keyed by display name. Most settings live there today; a small set of legacy values (last-loaded machine, per-machine last-inserted disk paths, audio download consent, window placement) still live in the registry for backwards compatibility and will migrate to JSON in a follow-up.

Disk II audio (v1.3.696)

Realistic mechanical sounds during disk activity, mixed into the WASAPI pipeline alongside the //e speaker:

  • Stereo motor hum, head-step clicks, track-0 / max-track bumps, and disk insert / eject sounds.
  • Per-drive equal-power stereo panning: single-drive profiles play centered; in two-drive profiles Drive 1 leans left, Drive 2 leans right.
  • Step-vs-seek discrimination: contiguous step bursts during DOS RWTS recalibration fuse into a continuous seek buzz instead of N overlapping clicks.
  • View → Options... dialog with a Drive Audio toggle (default on) and a Disk II mechanism dropdown (Shugart SA400 by default, or Alps 2124A). Both persist per-machine via the registry.
  • First-run consent dialog downloads the actual recordings from the OpenEmulator project; OGGs are decoded in memory via vendored stb_vorbis and written as WAV (no .ogg retained on disk). Asked once per machine, persisted thereafter.
  • Generic IDriveAudioSink / IDriveAudioSource / DriveAudioMixer abstraction so future drive types (//c internal 5.25, DuoDisk, Apple 5.25 Drive, ProFile, ...) plug in without touching the mixer.

Project Structure

Casso.sln
├── CassoCore/     Static library — CPU emulator, assembler, parser, opcode table
├── CassoEmuCore/  Static library — Apple II devices, video modes, audio generator + drive-audio mixer
├── Casso/         Win32 application — Apple II platform emulator (D3D11, WASAPI, Disk II audio)
├── CassoCli/      Console application — AS65-compatible assembler CLI with `run` subcommand
└── UnitTest/      Test DLL — Microsoft Native CppUnitTest (1500+ tests)

Requirements

  • Windows 10/11
  • PowerShell 7 (pwsh) for build/test scripts
  • Visual Studio 2026 (v18.x)
    • Workload: Desktop development with C++
    • Components: MSVC build tools, Windows SDK, C++ unit test framework
    • Optional: MSVC ARM64 build tools (for ARM64 builds)
  • Optional: VS Code (repo includes .vscode/ tasks)

Quick Start

Build

# Build Debug for current architecture (Ctrl+Shift+B in VS Code)
.\scripts\Build.ps1

# Build Release
.\scripts\Build.ps1 -Configuration Release

# Build all platforms
.\scripts\Build.ps1 -Target BuildAllRelease

# Rebuild with code analysis (warnings as errors)
.\scripts\Build.ps1 -Configuration Release -RunCodeAnalysis

Test

# Build and run tests
.\scripts\RunTests.ps1

# Or use VS Code: Run Tests (current arch)

Assemble and Run

# Assemble a source file to a flat binary (AS65 mode — no subcommand)
CassoCli input.a65 -o output.bin

# Assemble with a listing file and a symbol table
CassoCli input.a65 -o output.bin -l listing.txt -t

# Output Motorola S-record (.s19) or Intel HEX (.hex)
CassoCli input.a65 -s   -o output.s19
CassoCli input.a65 -s2  -o output.hex

# Pre-define a symbol on the command line
CassoCli input.a65 -d DEBUG=1 -o output.bin

# Generate a listing with cycle counts
CassoCli input.a65 -c -l listing.txt

# Assemble and run an assembly source directly
CassoCli run input.a65

# Load and run a pre-assembled binary at a specific address
CassoCli run output.bin --load $8000

Apple II Emulator

The emulator requires Apple II ROM images, which are copyrighted by Apple and not distributed with this project. A script is included to download them from the AppleWin project:

# Download ROM images into the per-machine Machines/<Name>/ folders
.\scripts\FetchRoms.ps1

# Run the emulator (defaults to Apple II+)
Casso

# Run with a specific machine config
Casso --machine Apple2e

ROM images live under Machines/<MachineName>/ (e.g., Machines/Apple2e/Apple2e.rom) and shared device boot ROMs live under Devices/<Family>/ (e.g., Devices/DiskII/Disk2.rom). Both Machines/ and Devices/ are fully runtime-managed: every file inside is either extracted from binary-embedded resources or downloaded on first launch (with user consent). Delete either directory and the next launch rebuilds it from scratch.

Available machine configs are in Machines/<MachineName>/<MachineName>.json.

Assembler Features

Feature Syntax / Flag
All 56 mnemonics LDA, STA, ADC, BNE, etc.
All addressing modes #$42, $30, $1234,X, ($20),Y, A
Labels loop: DEX / BNE loop
Directives .org $8000, .byte $FF, .word $1234, .text "hello", code/data/bss
Constants value = $42, carry equ %00000001 (chains and forward refs supported)
Conditionals if/ifdef/ifndef/else/endif
Macros name macroendm, with arguments and \ line continuation
Includes include "file.a65"
Comments ; full line / LDA #$42 ; inline
Number formats $FF (hex), %10101010 (binary), 255 (decimal)
Expressions full operator set: + - * / % & | ^ ~ << >>, <label, >label, current-PC *
Listing output -l [file] (stdout or file), -c for cycle counts, -m for macro expansion
Symbol table -t
Output formats flat binary (default), -s (S-record), -s2 (Intel HEX)
Fill control -z for $00 fill (default $FF)
Pre-defined symbols -d NAME or -d NAME=VALUE
Debug info -g [file]
Warning control --warn, --no-warn, --fatal-warnings
Verbose / quiet -v / -q
Flag concatenation -tlfile-t -l file (AS65 style)

CPU Emulation Status

All 56 standard 6502 mnemonics are implemented. Validated against Klaus Dormann's functional test suite (full pass) and Tom Harte's SingleStepTests (all 151 legal-opcode test sets, 10,000 vectors each).

Roadmap

Done

  • Pass Klaus Dormann's 6502 functional test suite (#7)
  • Per-opcode validation against Tom Harte's SingleStepTests (#29, #38)
  • Apple //e fidelity — cold boot to BASIC, audit-correct Language Card, 64 KB aux RAM, 80-column text + Double Hi-Res, soft reset vs. power cycle, IRQ/NMI dispatch, RDVBLBAR
  • Disk II controller — DOS 3.3 / ProDOS .dsk / .do / .po nibblization + WOZ v1 / v2 with auto-flush on eject
  • Disk II mechanical audio — stereo motor hum, head-step clicks, track-0 bump, disk insert / eject sounds, with a runtime Settings → Machine → Drive audio toggle. Built on a generic IDriveAudioSink / IDriveAudioSource / DriveAudioMixer abstraction so future drive types (//c internal 5.25, DuoDisk, ProFile, …) plug in without touching the mixer
  • Headless test harness for deterministic integration tests (HeadlessHost, framebuffer scraper, keyboard injector)
  • Performance gate — emulator throughput budget enforced in CI (Release-only)
  • Cycle-accurate execution and profiling (#57)

Medium Priority

  • 65C02 extended instruction support, with assembler --cpu flag (#9)
  • Undocumented / illegal opcode support (#52)
  • Rockwell / WDC 65C02 variants (#49, #50)
  • Disk II copy-protection fidelity — spin-up delay, cycle-accurate rotational position, bit-level write path (#67)
  • Boot Karateka from its WOZ image (RWTS18 copy protection) (#68)
  • Choplifter gameplay starts after the title screen (WOZ copy protection) (#69)
  • Boot Lode Runner from its WOZ image (copy protection) (#70)

Low Priority

  • NES 6502 / Ricoh 2A03 variant (#47)
  • Example programs — ready-to-assemble demos and tutorials (#55)
  • VS Code extension — syntax highlighting, assemble-on-save, inline diagnostics (#54)
  • Interactive debugger / monitor — step, breakpoints, register watch, memory dump (#51)
  • Relocatable object output — o65 format for cc65 toolchain integration (#58)

Why "Casso"?

While emu is the more obvious name and mascot for an emulator, I wanted Casso to stand out; to be just a little weird; to think different. I picked its larger, flightless, considerably more dangerous cousin: the cassowary—Casso to his friends.

I thus present to you our regal namesake—revel in his splendor!

Southern Cassowary

Cassowary photo by Mr. Smiley / BunyipCo, licensed under CC BY-NC-SA 3.0.

Acknowledgments

Casso's correctness is validated against two exceptional open-source test suites:

  • Klaus Dormann's 6502 Functional Test Suite@Klaus2m5's exhaustive functional test exercises every documented 6502 behavior: all instructions, addressing modes, flag interactions, BCD arithmetic, and edge cases. Casso passes the full suite.
  • Tom Harte's SingleStepTests@TomHarte's per-opcode test vectors validate every legal 6502 opcode against cycle-accurate reference traces. Casso passes all 151 legal-opcode test sets (10,000 vectors each).

Thank you to both authors for making these invaluable resources freely available. They are the gold standard for 6502 emulator validation.

Contributing

See CONTRIBUTING.md for commit conventions, build instructions, code style guidelines, and other contributor guidelines.

License

MIT

Packages

 
 
 

Contributors