rigless 0.2.0

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model and unit of virtual time in Rust, and a CPU core of its own that translates the firmware's instructions to host code
Documentation
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Memory laid out by hand for a test to read, little-endian as the part keeps it, which answers
//! only where something was laid.

use std::collections::BTreeMap;

use crate::emulator::{Error, Memory};
use crate::soc::Width;

/// Bytes at addresses, nothing anywhere else.
#[derive(Debug, Default)]
pub(crate) struct Laid(pub(crate) BTreeMap<u64, u8>);

impl Laid {
    pub(crate) fn bytes(&mut self, address: u64, bytes: &[u8]) {
        for (at, &byte) in (address..).zip(bytes) {
            self.0.insert(at, byte);
        }
    }

    pub(crate) fn word(&mut self, address: u64, value: u32) {
        self.bytes(address, &value.to_le_bytes());
    }

    pub(crate) fn read(&self, address: u64, width: Width) -> Option<u64> {
        (0..u64::from(width.bytes()))
            .rev()
            .try_fold(0, |value, offset| {
                Some(value << 8 | u64::from(*self.0.get(&(address + offset))?))
            })
    }
}

impl Memory for Laid {
    fn read(&mut self, address: u64, width: Width) -> Result<u64, Error> {
        Laid::read(self, address, width).ok_or(Error::Memory { address, width })
    }

    fn write(&mut self, address: u64, width: Width, value: u64) -> Result<(), Error> {
        let bytes = value.to_le_bytes();
        self.bytes(address, &bytes[..width.bytes() as usize]);
        Ok(())
    }
}