devices6502
A Rust library providing composable, memory-mapped device implementations for 6502-based systems. This crate enables developers to build flexible memory maps for 6502 emulators, simulators, and other retro computing projects.
Features:
- Fully composable, type-safe memory device implementations
- Generic RAM and ROM devices with power-of-2 sizing constraints
- Device mirroring, hot-swappable sockets, and transparent composition
- Zero-copy operations for efficient memory access
- Comprehensive test coverage for all device types and edge cases
no_stdcompatible core
Quick facts:
- All devices implement the unified
Devicetrait for consistent interfaces - Memory sizes are constrained to powers of 2 and capped at 64KB per device
- Hot-swap sockets enable dynamic device insertion/removal at runtime
- Address space composition (Adjacent) scales devices across full 16-bit address ranges
Contents
- Getting started
- Device Types
- Building a 6502 Memory Map
- Performance Considerations
- Testing
- Development notes
Getting started
Device Types
- RAM & ROM Devices: Generic, configurable memory devices with power-of-2 sizing up to 64KB
- Memory Mirroring: Repeat devices multiple times in the address space for efficient memory mapping
- Hot-Swappable Sockets: Dynamically connect/disconnect devices at runtime (perfect for cartridge slots)
- Device Composition: Combine multiple devices (e.g., separate read/write paths)
- Zero-Copy Operations: Efficient memory operations with minimal overhead
- Type-Safe Configuration: Compile-time guarantees for memory sizes via const generics
- Comprehensive Testing: 16+ unit tests covering edge cases and panic conditions
Quick Start
Add to your Cargo.toml:
[]
= "0.1"
Basic Usage
Ram
Random Access Memory with read/write support.
use ;
let mut ram = new;
ram.write;
assert_eq!;
Constraints:
- Size must be a power of 2
- Maximum size: 64KB (SIZE_64K)
- Addresses wrap within device bounds
Rom
Read-Only Memory; write operations are silently ignored.
use ;
let data = vec!;
let mut rom = with_data;
// Read succeeds
assert_eq!;
// Write is ignored
rom.write;
assert_eq!;
Mirror<T, N>
Repeat a device N times in the address space. Useful when a small device needs to appear multiple times in memory.
use ;
// Create a mirrored device: 1KB RAM repeated 4 times (total 4KB)
type MirroredRam = ;
let mut mirrored = new;
// Writing to address 0 and 1024 affects the same underlying RAM location
mirrored.write;
assert_eq!; // Mirrored at offset 1024
Constraints:
- N must be a power of 2 and ≥ 2
- Total address space must fit within 16 bits
Socket<ADDR_BITS>
A hot-swappable device socket for dynamic device insertion/removal at runtime.
use ;
let mut socket = new; // 14 address bits = 16KB address space
// Create and insert a device
let ram = Boxnew;
socket.connect_device;
// Use the socket like any other device
socket.write;
assert_eq!;
// Hot-swap: remove and replace device
let old_device = socket.disconnect_device;
let new_ram = Boxnew;
socket.connect_device;
Adjacent<T1, T2>
Combine two devices side-by-side in the address space.
use ;
type MemoryMap = ;
let mut memory = new;
// First 32KB is RAM (0x0000-0x7FFF)
memory.write;
assert_eq!;
// Second 32KB is ROM (0x8000-0xFFFF) - writes are ignored
memory.write;
ReadWrite<READ, WRITE>
Route read and write operations to different devices.
use ;
let rom_data = vec!;
let rom = with_data;
let mut ram = new;
let mut device = new;
assert_eq!; // Reads from ROM
device.write; // Writes to RAM
Common Memory Sizes
Convenient constants for standard sizes:
use *;
let _: = new; // 1 KB
let _: = new; // 2 KB
let _: = new; // 4 KB
let _: = new; // 8 KB
let _: = new; // 16 KB
let _: = new; // 32 KB
let _: = new; // 64 KB
Device Trait
All devices implement the Device trait:
Building a 6502 Memory Map
Composing multiple devices into a complete 64KB memory map:
use ;
// Lower 32KB: RAM
type LowerMemory = ;
// Upper 32KB: 8KB ROM mirrored 4 times
type UpperMemory = ;
// Complete 64KB address space
type MemoryMap = ;
let mut memory = new;
// Initialize ROM
let rom_data = vec!;
// Use the memory
memory.write; // Write to RAM
assert_eq!;
memory.write; // Write to ROM (ignored)
Panic Behavior
The crate will panic in the following scenarios:
- Invalid device sizes: Creating RAM with size 0, not a power of 2, or > 64KB
- Invalid mirror parameters: Creating Mirror with N < 2, N not power of 2, or total space > 64KB
- Socket mismatches: Connecting a device with incompatible address bits
- Empty socket operations: Reading/writing from an empty socket
These panics are intentional to catch configuration errors at development time.
Performance Considerations
- Inline storage: RAM and ROM store data inline; no allocations
- Address wrapping: Automatic wrapping for devices smaller than 16 bits
- Generic specialization: Const generics enable compiler optimization and monomorphization
- Zero-copy reads: Read operations don't allocate; use
cache_current_read_data()for bulk operations
Testing
Run the full test suite:
Tests cover:
- RAM initialization and address wrapping
- ROM write rejection
- Device mirroring and composition
- Socket hot-swapping
- Memory mapping patterns
Development notes
-
Module layout
device: Device trait and core typesram,rom: Standard memory implementationsmirror: Device repetition in address spacesocket: Hot-swappable device containeradjacent: Side-by-side device compositionreadwrite: Asymmetric read/write routingsize_const: Common memory size constants
-
Design philosophy
- Prefer compile-time guarantees (const generics) over runtime checks
- Use type-safe composition for flexible memory mapping
- Panic on configuration errors to catch bugs early
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.