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
//! The chip end of an inter-integrated-circuit bus.

use std::any::Any;

use crate::chip::Timeline;
use crate::snapshot::Store;
use crate::time::EventId;

/// An integrated circuit on an I2C bus: what a controller, on a part or inside another chip, addresses.
///
/// A transaction is a start carrying an address and a direction, bytes one way, and a stop. A
/// restart is another start without the stop. The controller calls this and never learns what's
/// behind it. A chip implements it and never learns there's a controller. A chip that isn't at the
/// address doesn't acknowledge the start, and the controller reads nothing from it.
///
/// `Any` so a controller with several peers on one bus can hand a board back the concrete chip it
/// hung there, the way [`crate::chip::spi::Peer`] and [`crate::chip::uart::Peer`] already do. A
/// snapshot keeps a chip through its own [`Store`].
pub trait Peer: Store + std::fmt::Debug + Send + Any {
    /// A start condition carrying `address` and whether the controller will read. Returns the
    /// acknowledge.
    fn start(&mut self, address: u8, read: bool, context: &mut Timeline<'_, '_>) -> bool;

    /// One byte from the controller, after a start it acknowledged for a write. Returns the
    /// acknowledge.
    fn write(&mut self, byte: u8, context: &mut Timeline<'_, '_>) -> bool;

    /// One byte to the controller, after a start it acknowledged for a read. `last` is the
    /// controller's not-acknowledge, which says the stop is next.
    fn read(&mut self, last: bool, context: &mut Timeline<'_, '_>) -> u8;

    /// The stop condition.
    fn stop(&mut self, context: &mut Timeline<'_, '_>);

    /// An event this chip scheduled through the [`Timeline`] it was handed, at its instant. The
    /// controller hands every event of its instance to every chip it carries, after its own, so a
    /// chip ignores an id it didn't ask for. A silenced chip hears its events all the same, since
    /// what it does with time goes on while it's cut off. Nothing, for a chip that schedules none.
    fn on_event(&mut self, _event: EventId, _context: &mut Timeline<'_, '_>) {}

    /// The part this chip hangs on restarted at the instant `context` gives, and every event the
    /// chip scheduled through its controller went with the reset, since a chip keeps running
    /// through its part's reset. A chip that keeps time schedules again here. Nothing, for a chip
    /// that schedules none.
    fn restarted(&mut self, _context: &mut Timeline<'_, '_>) {}

    /// Every byte this peer has been written, oldest first, or nothing where it keeps no log.
    ///
    /// The far end's own record, the same job [`crate::chip::spi::Peer::received`] and
    /// [`crate::chip::uart::Peer::received`] do for their own buses: it is what says a byte reached
    /// the far end, rather than the controller's own belief that it sent one.
    fn received(&self) -> &[u8] {
        &[]
    }
}