devices6502 0.1.0

Helper library for cpu6502 implementing memory devices
Documentation
# devices6502


[![Crates.io](https://img.shields.io/crates/v/devices6502.svg)](https://crates.io/crates/devices6502)
[![Documentation](https://docs.rs/devices6502/badge.svg)](https://docs.rs/devices6502)
[![License](https://img.shields.io/crates/l/devices6502.svg)](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.