4 releases (2 breaking)
Uses new Rust 2024
| new 0.8.0 | Dec 8, 2025 |
|---|---|
| 0.7.1 | Dec 4, 2025 |
| 0.7.0 | Dec 3, 2025 |
| 0.6.1 | Nov 21, 2025 |
#48 in Emulators
Used in 6 crates
(4 directly)
195KB
3K
SLoC
ym2149-ay-replayer – ZXAY/EMUL player with Z80 emulation
Cycle-accurate Project AY (.ay) playback for Rust. This crate parses
the original ZXAY/EMUL container, reconstructs the signed
pointer-based structures (header, subsongs, points, block tables) and
runs the embedded Z80 player inside a pure Rust environment powered by
iz80 and the workspace’s
ym2149 chip.
Project AY delivered thousands of Spectrum and CPC rips that bundle a tiny Z80 replay as part of the file. Instead of shipping bespoke shims, this crate emulates that player verbatim – including the Motorola-endian headers, signed block offsets, and port wiring for ZX/CPC machines.
Highlights
- 🧾 ZXAY parser – validates header signatures, extracts metadata, subsongs, NT strings, and memory block layouts.
- 🧠 Z80 execution – runs the bundled player using the iz80 core, including INIT/INTERRUPT entries, stack setup, register presets, and per-frame interrupts.
- 🎹 Real PSG bridge – wired to the shared
ym2149backend so the CLI, Bevy plugin, exporter, and wasm builds all hear the same output. - 🕹 CPC + Spectrum – detects PPI-style port access (
#F4xx/#F6xx) and re-tunes the PSG clock for 1 MHz CPC rips while keeping 2 MHz for ZX files. - 📦 ProjectAY fixtures – unit tests load real songs
(
SpaceMadness.AY,impact demo 3_2.ay) to guard against parser or emulator regressions. - 🔌 Reusable player –
AyPlayerimplements the sameRealtimeChip/PlaybackControllertraits as the YM and Arkos players, so downstream code just boxes one more variant.
Quick Start
[dependencies]
ym2149-ay-replayer = "0.7"
# Optional but common when you plan to pipe the samples into the rest of the stack
ym2149 = "0.7"
use ym2149_ay_replayer::{load_ay, AyPlayer};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = std::fs::read("ProjectAY/Spectrum/Demos/SpaceMadness.AY")?;
let ay = load_ay(&data)?;
println!("File version: {}", ay.header.file_version);
println!("Songs: {}", ay.songs.len());
let (mut player, meta) = AyPlayer::load_from_bytes(&data, ay.header.first_song_index as usize)?;
println!("Playing: {}", meta.song_name);
player.play()?;
let samples = player.generate_samples(882); // ≈ one 50 Hz frame @ 44.1 kHz
println!("Rendered {} samples", samples.len());
Ok(())
}
Firmware Limitations
The player only handles AY drivers that run entirely out of their own code/data; once a track calls into the Spectrum or CPC ROM jump table we stop playback and report that firmware emulation is unsupported. ZX files that stay self-contained work fine; CPC AY rips and ROM-heavy ZX rips should be played in a full emulator instead.
When to use it
- Integrate
.ayplayback into tooling alongside.ymand.aksfiles - Feed AY songs through the CLI (
ym2149-replayer-cli), Bevy plugin, or wasm demo without branching per format - Inspect AY headers / block layouts when writing conversion pipelines
API Surface
load_ay→ low-level parser returningAyFile(header metadata, song list, block descriptors)AyPlayer→ Z80 + PSG player with familiarplay/pause/stop/generate_samplesmethodsAyMetadata→ descriptive info for UIs/inspectorsAyMachine→ host implementation of the AY memory map + PSG port bridging
The player mirrors the workspace conventions: it is Send, implements
PlaybackController, exposes mute toggles, register snapshots, and
playback position helpers.
Relationship to the Workspace
- CLI (
ym2149-replayer-cli): file detection now routes.ym,.aks, and.ayto the correct player, reusing the same streaming- visualization stack.
- Bevy (
bevy_ym2149):YmSongPlayergained anAyvariant so assets dropped into Bevy can be YM/AKS/AY without code changes. - WASM (
ym2149-wasm): browser player auto-detects AY files and uses the sameAyPlayerunder the hood. - Docs/Examples: Project AY fixtures live in
/ProjectAY/and are used by both tests and the stats helper inexamples/stats.rs.
Testing
# Parser + player tests (uses bundled AY fixtures)
cargo test -p ym2149-ay-replayer
# Optional helper to scan fixture stats (interrupt usage, etc.)
cargo run -p ym2149-ay-replayer --example stats -- ProjectAY
The fixtures cover both Spectrum and CPC titles so we don’t regress on port wiring, pointer arithmetic, or INIT/INTERRUPT fallbacks.
Have more AY test material? Drop it under ProjectAY/ and extend the
unit tests – the more demoscene rips we cover, the safer future
refactors become. Questions/bugs? Open an issue on the main repo. Happy
tuning!
CPC Firmware Notes
Some .ay files target the Amstrad CPC firmware: they talk to the PSG
via the PPI (#F4xx/#F6xx) and rely on jump tables, AMSDOS buffers, and
keyboard handlers that live in the CPC ROM/RAM workspace. Emulating that
environment correctly requires bundling the original ROMs and a sizable
portion of the CPC OS, which is well beyond the scope of this crate (and
comes with legal/licencing uncertainties). As a result CPC-based AY
files are currently not supported: the player will detect the CPC port
traffic, stop playback, and report that firmware emulation is missing.
ZX Spectrum AY rips continue to work normally; if you need CPC playback, use a full CPC emulator or a specialised player that ships the firmware.
Dependencies
~280–700KB
~16K SLoC