# devices6502
[](https://crates.io/crates/devices6502)
[](https://docs.rs/devices6502)
[](https://github.com/CasiussDev/devices6502/blob/main/LICENSE-APACHE)
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_std` compatible core
**Quick facts:**
- All devices implement the unified `Device` trait 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](#getting-started)
- [Device Types](#device-types)
- [Building a 6502 Memory Map](#building-a-6502-memory-map)
- [Performance Considerations](#performance-considerations)
- [Testing](#testing)
- [Development notes](#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`:
```toml
[dependencies]
devices6502 = "0.1"
```
### Basic Usage
### Ram<SIZE>
Random Access Memory with read/write support.
```rust
use devices6502::{Ram, SIZE_64K, Device};
let mut ram = Ram::<SIZE_64K>::new();
ram.write(0x42, 0x0000);
assert_eq!(ram.read(0x0000), 0x42);
```
**Constraints:**
- Size must be a power of 2
- Maximum size: 64KB (SIZE_64K)
- Addresses wrap within device bounds
### Rom<SIZE>
Read-Only Memory; write operations are silently ignored.
```rust
use devices6502::{Rom, SIZE_8K, Device};
let data = vec![0xFF; SIZE_8K];
let mut rom = Rom::<SIZE_8K>::with_data(&data);
// Read succeeds
assert_eq!(rom.read(0x0000), 0xFF);
// Write is ignored
rom.write(0x00, 0x0000);
assert_eq!(rom.read(0x0000), 0xFF);
```
### Mirror<T, N>
Repeat a device N times in the address space. Useful when a small device needs to appear multiple times in memory.
```rust
use devices6502::{Mirror, Ram, Device, SIZE_1K};
// Create a mirrored device: 1KB RAM repeated 4 times (total 4KB)
type MirroredRam = Mirror<Ram<SIZE_1K>, 4>;
let mut mirrored = MirroredRam::new();
// Writing to address 0 and 1024 affects the same underlying RAM location
mirrored.write(0x42, 0x0000);
assert_eq!(mirrored.read(0x0400), 0x42); // 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.
```rust
use devices6502::{Socket, Ram, Device, SIZE_16K};
let mut socket = Socket::<14>::new(); // 14 address bits = 16KB address space
// Create and insert a device
let ram = Box::new(Ram::<SIZE_16K>::new());
socket.connect_device(ram);
// Use the socket like any other device
socket.write(0x42, 0x0000);
assert_eq!(socket.read(0x0000), 0x42);
// Hot-swap: remove and replace device
let old_device = socket.disconnect_device();
let new_ram = Box::new(Ram::<SIZE_16K>::new());
socket.connect_device(new_ram);
```
### Adjacent<T1, T2>
Combine two devices side-by-side in the address space.
```rust
use devices6502::{Adjacent, Ram, Rom, Device, SIZE_32K};
type MemoryMap = Adjacent<Ram<SIZE_32K>, Rom<SIZE_32K>>;
let mut memory = MemoryMap::new();
// First 32KB is RAM (0x0000-0x7FFF)
memory.write(0x42, 0x0000);
assert_eq!(memory.read(0x0000), 0x42);
// Second 32KB is ROM (0x8000-0xFFFF) - writes are ignored
memory.write(0x99, 0x8000);
```
### ReadWrite<READ, WRITE>
Route read and write operations to different devices.
```rust
use devices6502::{ReadWrite, Ram, Rom, Device, SIZE_4K};
let rom_data = vec![0xFF; SIZE_4K];
let rom = Rom::<SIZE_4K>::with_data(&rom_data);
let mut ram = Ram::<SIZE_4K>::new();
let mut device = ReadWrite::new(rom, ram);
assert_eq!(device.read(0x0000), 0xFF); // Reads from ROM
device.write(0x42, 0x0000); // Writes to RAM
```
## Common Memory Sizes
Convenient constants for standard sizes:
```rust
use devices6502::*;
let _: Ram<SIZE_1K> = Ram::new(); // 1 KB
let _: Ram<SIZE_2K> = Ram::new(); // 2 KB
let _: Ram<SIZE_4K> = Ram::new(); // 4 KB
let _: Ram<SIZE_8K> = Ram::new(); // 8 KB
let _: Ram<SIZE_16K> = Ram::new(); // 16 KB
let _: Ram<SIZE_32K> = Ram::new(); // 32 KB
let _: Ram<SIZE_64K> = Ram::new(); // 64 KB
```
## Device Trait
All devices implement the `Device` trait:
```rust
pub trait Device {
fn with_data(data: &[u8]) -> Self;
fn init_data(&mut self, data: &[u8]);
fn cache_current_read_data(&self, destination: &mut [u8]);
fn read(&self, addr: u16) -> u8;
fn write(&mut self, data: u8, addr: u16);
fn addr_space_size() -> u32;
fn addr_bits_count() -> u8;
}
```
## Building a 6502 Memory Map
Composing multiple devices into a complete 64KB memory map:
```rust
use devices6502::{Adjacent, Ram, Rom, Mirror, Device, SIZE_32K, SIZE_8K};
// Lower 32KB: RAM
type LowerMemory = Ram<SIZE_32K>;
// Upper 32KB: 8KB ROM mirrored 4 times
type UpperMemory = Mirror<Rom<SIZE_8K>, 4>;
// Complete 64KB address space
type MemoryMap = Adjacent<LowerMemory, UpperMemory>;
let mut memory = MemoryMap::new();
// Initialize ROM
let rom_data = vec![0xFF; SIZE_8K];
// Use the memory
memory.write(0x42, 0x0000); // Write to RAM
assert_eq!(memory.read(0x0000), 0x42);
memory.write(0x99, 0x8000); // 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:
```bash
cargo test
```
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 types
- `ram`, `rom`: Standard memory implementations
- `mirror`: Device repetition in address space
- `socket`: Hot-swappable device container
- `adjacent`: Side-by-side device composition
- `readwrite`: Asymmetric read/write routing
- `size_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.