use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use crate::component::{BaseComponent, Component}; use crate::pin::{Pin, PinValue}; /// Common memory operation states for the unified Intel 400x state machine /// /// This enum represents the basic states that all Intel 400x components go through /// during memory operations. These states form the foundation of the unified state /// machine architecture. #[derive(Debug, Clone, Copy, PartialEq)] pub enum MemoryState { /// No memory operation in progress /// /// In this state, the component is idle and waiting for a chip select or /// other control signal to initiate a new operation. Idle, /// Currently latching address nibbles /// /// During this state, the component is receiving the address in two 4-bit /// phases (high nibble first, then low nibble) from the CPU. AddressPhase, /// Address latched, waiting for access time /// /// After the complete address is received, the component waits for the /// required access time before proceeding with the operation. WaitLatency, /// Latency elapsed, driving data on bus /// /// In this state, the component drives data onto the bus (for read operations) /// or accepts data from the bus (for write operations). DriveData, } /// Common timing constants for Intel 400x series /// /// This struct provides standardized timing values for the Intel 400x series components. /// These constants are based on the original Intel 4004 family specifications and /// ensure consistent timing behavior across all components. pub struct TimingConstants; impl TimingConstants { /// Default access time for most memory operations (500ns) /// /// This is the standard access time for ROM and RAM operations /// based on the original Intel 4004 timing specifications. pub const DEFAULT_ACCESS_TIME: Duration = Duration::from_nanos(500); // 500ns default /// Fast access time for shift register operations (200ns) /// /// The Intel 4003 shift register has faster access times compared to /// ROM and RAM components due to its simpler design. pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers /// Address setup time before clock edge (100ns) /// /// Minimum time that address must be stable before the clock edge /// to ensure proper latching by the component. pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time /// Data valid delay after clock edge (200ns) /// /// Time from clock edge until data is guaranteed to be valid on the bus /// during read operations. pub const DATA_VALID: Duration = Duration::from_nanos(200); // Data valid delay } /// Common clock edge detection and timing functionality pub trait Intel400xClockHandling { fn get_base(&self) -> &BaseComponent; /// Read the two-phase clock pins from CPU /// Returns: (PHI1_value, PHI2_value) fn read_clock_pins(&self) -> (PinValue, PinValue) { let phi1 = self.get_clock_pin("PHI1"); let phi2 = self.get_clock_pin("PHI2"); (phi1, phi2) } /// Get a clock pin value safely fn get_clock_pin(&self, name: &str) -> PinValue { if let Ok(pin) = self.get_base().get_pin(name) { if let Ok(pin_guard) = pin.lock() { pin_guard.read_immediate() } else { PinValue::Low } } else { PinValue::Low } } /// Check for Φ1 rising edge fn is_phi1_rising_edge(&self, prev_phi1: PinValue) -> bool { let (phi1, _) = self.read_clock_pins(); phi1 == PinValue::High && (prev_phi1 == PinValue::Low || prev_phi1 == PinValue::HighZ) } /// Check for Φ1 falling edge fn is_phi1_falling_edge(&self, prev_phi1: PinValue) -> bool { let (phi1, _) = self.read_clock_pins(); phi1 == PinValue::Low && prev_phi1 == PinValue::High } /// Check for Φ2 rising edge fn is_phi2_rising_edge(&self, prev_phi2: PinValue) -> bool { let (_, phi2) = self.read_clock_pins(); phi2 == PinValue::High && prev_phi2 == PinValue::Low } /// Check for Φ2 falling edge fn is_phi2_falling_edge(&self, prev_phi2: PinValue) -> bool { let (_, phi2) = self.read_clock_pins(); phi2 == PinValue::Low && prev_phi2 == PinValue::High } /// Update clock state tracking for edge detection fn update_clock_states(&self, prev_phi1: &mut PinValue, prev_phi2: &mut PinValue) { let (phi1, phi2) = self.read_clock_pins(); *prev_phi1 = phi1; *prev_phi2 = phi2; } } /// Common data bus operations pub trait Intel400xDataBus { fn get_base(&self) -> &BaseComponent; /// Read the 4-bit data bus from D0-D3 pins /// Returns: 4-bit value from data bus pins fn read_data_bus(&self) -> u8 { let mut data = 0; for i in 0..4 { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(pin_guard) = pin.lock() { if pin_guard.read_immediate() == PinValue::High { data |= 1 << i; } } } } data & 0x0F } /// Drive the 4-bit data bus with the specified value /// Parameters: data - 4-bit value to drive on D0-D3 pins fn write_data_bus(&self, data: u8) { let nibble = data & 0x0F; for i in 0..4 { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(mut pin_guard) = pin.lock() { let bit_value = (nibble >> i) & 1; let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; pin_guard.set_driver( Some(format!("{}_DATA", self.get_base().get_name())), pin_value, ); } } } } /// Set data bus to high-impedance state to avoid bus contention fn tri_state_data_bus(&self) { for i in 0..4 { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(mut pin_guard) = pin.lock() { pin_guard.set_driver( Some(format!("{}_DATA", self.get_base().get_name())), PinValue::HighZ, ); } } } } } /// Common address handling functionality pub trait Intel400xAddressHandling { fn get_base(&self) -> &BaseComponent; /// Assemble complete 8-bit address from high and low nibbles /// Hardware: Intel 4004 provides address in two 4-bit phases /// Format: (high_nibble << 4) | low_nibble fn assemble_full_address( &self, high_nibble: Option, low_nibble: Option, ) -> Option { if let (Some(high), Some(low)) = (high_nibble, low_nibble) { Some(((high as u16) << 4) | (low as u16)) } else { None } } /// Handle address nibble latching during address phase /// Returns: (high_nibble, low_nibble, address_ready) /// /// After the full address is latched (both nibbles received), the nibbles /// are cleared to prepare for the next address cycle. The full address /// should be assembled and stored before calling this function returns. fn handle_address_latching( &self, nibble: u8, address_high_nibble: &mut Option, address_low_nibble: &mut Option, full_address_ready: &mut bool, address_latch_time: &mut Option, _access_time: Duration, ) { if address_high_nibble.is_none() { // First cycle: latch high nibble (bits 7-4) *address_high_nibble = Some(nibble); } else if address_low_nibble.is_none() { // Second cycle: latch low nibble (bits 3-0) *address_low_nibble = Some(nibble); // Mark address as ready and record latch time *full_address_ready = true; *address_latch_time = Some(Instant::now()); // Clear nibbles after full address is latched to prepare for next cycle // The full address should be assembled from the nibbles before this returns // (e.g., by the caller reading the nibble values before they're cleared) *address_high_nibble = None; *address_low_nibble = None; } } /// Handle latency timing during wait state /// Returns: true if latency has elapsed fn handle_latency_wait( &self, address_latch_time: &Option, access_time: Duration, ) -> bool { if let Some(latch_time) = address_latch_time { latch_time.elapsed() >= access_time } else { false } } } /// Common control pin reading functionality pub trait Intel400xControlPins { fn get_base(&self) -> &BaseComponent; /// Read SYNC pin state fn read_sync_pin(&self) -> bool { if let Ok(pin) = self.get_base().get_pin("SYNC") { if let Ok(pin_guard) = pin.lock() { pin_guard.read_immediate() == PinValue::High } else { false } } else { false } } /// Read CM-ROM pin state fn read_cm_rom_pin(&self) -> bool { // Note: CM pin name varies by component if let Ok(pin) = self.get_base().get_pin("CM") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { false } } else { false } } /// Read the CI (I/O select) pin state fn read_ci_pin(&self) -> bool { if let Ok(pin) = self.get_base().get_pin("CI") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { false } } else { false } } /// Read RESET pin state /// Hardware: RESET is active LOW (pin LOW = reset active) fn read_reset_pin(&self) -> bool { if let Ok(pin) = self.get_base().get_pin("RESET") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active } else { false } } else { false } } } /// Common reset handling functionality pub trait Intel400xResetHandling { fn get_base(&self) -> &BaseComponent; /// Handle system reset signal /// Hardware: RESET pin clears all internal state and tri-states outputs /// Note: RESET is active LOW (pin LOW = reset active) fn handle_reset(&mut self, reset_pin_name: &str) -> bool { let reset = if let Ok(pin) = self.get_base().get_pin(reset_pin_name) { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active } else { false } } else { false }; if reset { self.perform_reset(); } reset } /// Perform the actual reset operations (to be implemented by each chip) fn perform_reset(&mut self); } /// Common timing state interface for the unified Intel 400x state machine pub trait Intel400xTimingState { fn get_timing_state(&self) -> MemoryState; fn set_timing_state(&mut self, state: MemoryState); fn get_address_latch_time(&self) -> Option; fn set_address_latch_time(&mut self, time: Option); fn get_full_address_ready(&self) -> bool; fn set_full_address_ready(&mut self, ready: bool); fn get_address_high_nibble(&self) -> Option; fn set_address_high_nibble(&mut self, nibble: Option); fn get_address_low_nibble(&self) -> Option; fn set_address_low_nibble(&mut self, nibble: Option); fn get_access_time(&self) -> Duration; } /// Component type enumeration for Intel 400x series /// /// This enum identifies the specific type of Intel 400x component being modeled. /// Each component type has unique characteristics and behavior that are handled /// by the unified state machine through the Intel400xComponentBehavior trait. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Intel400xComponentType { /// Intel 4001 ROM (Read-Only Memory) chip /// /// The 4001 is a 256-byte ROM chip that stores program instructions. /// It responds to address transmission and provides read-only data access. Intel4001, /// Intel 4002 RAM (Random Access Memory) chip /// /// The 4002 is a 320-bit RAM chip organized as four registers of 20 bits each. /// It supports both read and write operations and includes I/O ports. Intel4002, /// Intel 4003 Shift Register chip /// /// The 4003 is a 10-bit static shift register used for I/O expansion. /// It operates differently from memory chips and ignores address transmission. Intel4003, /// Intel 4004 CPU (Central Processing Unit) chip /// /// The 4004 is the 4-bit microprocessor that controls the system. /// It generates addresses and control signals for other components. Intel4004, } /// Unified state enumeration for all Intel 400x components /// /// This enum represents all possible states for any Intel 400x component in the /// unified state machine architecture. It includes common states shared by all /// components as well as component-specific states for specialized operations. /// /// The unified state machine uses this enum to provide a consistent interface /// while still allowing component-specific behavior through the Intel400xComponentBehavior trait. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Intel400xUnifiedState { // Common states (shared across all components) /// Idle state - component is waiting for operation /// /// In this state, the component is not actively processing any operation /// and is waiting for a chip select or other control signal. Idle, /// Address phase - latching address from CPU /// /// The component is receiving address information in two 4-bit phases /// (high nibble first, then low nibble) from the CPU. AddressPhase, /// Wait latency - waiting for access time to elapse /// /// After receiving the complete address, the component waits for the /// required access time before proceeding with the operation. WaitLatency, /// Drive data - transferring data on the bus /// /// The component is actively driving data onto the bus (for read operations) /// or accepting data from the bus (for write operations). DriveData, // Component-specific states // Intel 4002 (RAM) specific states /// Read data state - RAM is reading from memory /// /// The RAM component is reading data from the specified address and /// preparing to drive it onto the data bus. ReadData, /// Write data state - RAM is writing to memory /// /// The RAM component is accepting data from the bus and writing it /// to the specified memory location. WriteData, /// Output port state - RAM is driving I/O ports /// /// The RAM component is outputting data to its I/O ports rather than /// the main data bus. OutputPort, // Intel 4003 (Shift Register) specific states /// Shift data state - shifting register contents /// /// The 4003 shift register is shifting its contents serially, /// either in or out depending on the operation. ShiftData, /// Load data state - loading parallel data /// /// The 4003 shift register is loading data in parallel format /// before beginning a shift operation. LoadData, // Intel 4004 (CPU) specific states /// Fetch instruction state - CPU is fetching instruction /// /// The CPU is fetching the next instruction from memory and /// preparing to decode and execute it. FetchInstruction, /// Execute instruction state - CPU is executing instruction /// /// The CPU is executing the currently fetched instruction and /// generating appropriate control signals. ExecuteInstruction, } /// State machine events that can trigger transitions /// /// This enum represents all possible events that can cause state transitions /// in the unified state machine. These events are generated by various sources /// including clock edges, control signals, and internal timing mechanisms. /// /// The state machine processes these events and determines the appropriate /// state transition based on the current state and component type. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Intel400xEvent { /// Φ1 (Phase 1) clock rising edge /// /// Generated when the first phase of the two-phase clock transitions /// from low to high. Used for timing control in various operations. ClockPhi1Rising, /// Φ1 (Phase 1) clock falling edge /// /// Generated when the first phase of the two-phase clock transitions /// from high to low. Often triggers address latching operations. ClockPhi1Falling, /// Φ2 (Phase 2) clock rising edge /// /// Generated when the second phase of the two-phase clock transitions /// from low to high. Used for data transfer operations. ClockPhi2Rising, /// Φ2 (Phase 2) clock falling edge /// /// Generated when the second phase of the two-phase clock transitions /// from high to low. Often triggers state transitions. ClockPhi2Falling, /// SYNC signal becomes active (high) /// /// The SYNC signal indicates the start of a new instruction cycle. /// Components use this to synchronize their operations with the CPU. SyncActive, /// SYNC signal becomes inactive (low) /// /// The SYNC signal indicates the end of an instruction cycle. /// Components may use this to reset their internal state. SyncInactive, /// Chip select becomes active /// /// Generated when a component is selected for an operation. /// This typically initiates a transition from Idle to AddressPhase. ChipSelect, /// Chip select becomes inactive /// /// Generated when a component is deselected. /// This typically forces a transition back to the Idle state. ChipDeselect, /// RESET signal becomes active (high) /// /// Generated when the system reset signal is asserted. /// All components transition to their initial state. ResetActive, /// RESET signal becomes inactive (low) /// /// Generated when the system reset signal is released. /// Components begin normal operation from their reset state. ResetInactive, /// Address latching complete /// /// Generated when both high and low address nibbles have been latched. /// Triggers transition from AddressPhase to WaitLatency. AddressComplete, /// Access latency has elapsed /// /// Generated when the required access time has passed after address latching. /// Triggers transition from WaitLatency to operation-specific states. LatencyElapsed, /// Data is ready for transfer /// /// Generated when data is available for reading or writing. /// Used in component-specific data transfer operations. DataReady, /// Operation is complete /// /// Generated when the current operation has finished successfully. /// Typically triggers transition back to the Idle state. OperationComplete, } /// Component behavior trait for implementing component-specific logic /// /// This trait defines the interface that all Intel 400x components must implement /// to work with the unified state machine architecture. It allows each component /// to provide its own specific behavior while still following the common /// state machine framework. /// /// # Unified State Machine Architecture /// /// The Intel 400x series uses a unified state machine approach that replaces /// individual chip state machines with a single, extensible framework. This /// approach provides several benefits: /// /// 1. **Consistency**: All components follow the same basic state transition patterns /// 2. **Maintainability**: Common logic is centralized in the state machine engine /// 3. **Extensibility**: New chip types can be added by implementing this trait /// 4. **Testability**: Components can be tested independently with mock behaviors /// /// # Implementing the Trait /// /// When implementing this trait for a new component type: /// /// 1. Return the appropriate component type from `get_component_type()` /// 2. Implement state entry/exit logic in `on_state_entry()` and `on_state_exit()` /// 3. Handle component-specific events in `handle_component_event()` /// 4. Execute state-specific logic in `execute_state_logic()` /// 5. Specify address handling behavior in `responds_to_address()` /// /// # Example /// /// ```rust /// struct MyCustomComponent { /// // Component fields... /// } /// /// impl Intel400xComponentBehavior for MyCustomComponent { /// fn get_component_type(&self) -> Intel400xComponentType { /// Intel400xComponentType::Intel4001 // Example type /// } /// /// fn on_state_entry(&mut self, state: Intel400xUnifiedState) { /// match state { /// Intel400xUnifiedState::AddressPhase => { /// // Prepare for address latching /// } /// Intel400xUnifiedState::DriveData => { /// // Prepare for data transfer /// } /// _ => {} /// } /// } /// /// // ... implement other methods /// } /// ``` pub trait Intel400xComponentBehavior { /// Get the component type /// /// Returns the specific Intel400xComponentType that this behavior implementation /// represents. This is used by the state machine to determine component-specific /// state transitions and behaviors. /// /// # Returns /// /// The Intel400xComponentType value identifying this component. fn get_component_type(&self) -> Intel400xComponentType; /// Handle component-specific state entry logic /// /// This method is called when the component enters a new state. Components /// should implement any initialization or preparation logic needed for the /// new state. /// /// # Parameters /// /// * `state` - The state being entered /// /// # Example /// /// ```rust /// fn on_state_entry(&mut self, state: Intel400xUnifiedState) { /// match state { /// Intel400xUnifiedState::AddressPhase => { /// // Reset address latching variables /// self.address_high_nibble = None; /// self.address_low_nibble = None; /// } /// Intel400xUnifiedState::DriveData => { /// // Prepare data for output /// self.prepare_output_data(); /// } /// _ => {} /// } /// } /// ``` fn on_state_entry(&mut self, state: Intel400xUnifiedState); /// Handle component-specific state exit logic /// /// This method is called when the component exits a state. Components /// should implement any cleanup or finalization logic needed before /// transitioning to the next state. /// /// # Parameters /// /// * `state` - The state being exited fn on_state_exit(&mut self, state: Intel400xUnifiedState); /// Handle component-specific event processing /// /// This method allows components to handle events that are specific to /// their operation before the general state machine processes them. /// If this method returns true, the event is considered handled and /// the state machine will not process it further. /// /// # Parameters /// /// * `event` - The event to handle /// /// # Returns /// /// `true` if the event was handled by the component, `false` if the /// state machine should continue processing the event. fn handle_component_event(&mut self, event: Intel400xEvent) -> bool; /// Execute component-specific state logic /// /// This method is called to execute the logic associated with the current /// state. Components should implement the actual work to be performed /// in each state here. /// /// # Parameters /// /// * `state` - The current state whose logic should be executed /// /// # Example /// /// ```rust /// fn execute_state_logic(&mut self, state: Intel400xUnifiedState) { /// match state { /// Intel400xUnifiedState::AddressPhase => { /// // Latch address nibble from data bus /// let nibble = self.read_data_bus(); /// self.latch_address_nibble(nibble); /// } /// Intel400xUnifiedState::DriveData => { /// // Drive data onto the bus /// let data = self.read_memory(self.current_address); /// self.write_data_bus(data); /// } /// _ => {} /// } /// } /// ``` fn execute_state_logic(&mut self, state: Intel400xUnifiedState); /// Check if the component should respond to address transmission /// /// This method determines whether the component should participate in /// address latching operations. Most components (4001, 4002, 4004) respond /// to address transmission, but the Intel 4003 shift register typically /// ignores it and goes directly to its data handling state. /// /// # Returns /// /// `true` if the component should respond to address transmission, /// `false` if it should ignore address transmission. /// /// # Default Behavior /// /// - Intel 4001 (ROM): `true` - Responds to address transmission /// - Intel 4002 (RAM): `true` - Responds to address transmission /// - Intel 4003 (Shift Register): `false` - Ignores address transmission /// - Intel 4004 (CPU): `true` - Responds to address transmission fn responds_to_address(&self) -> bool; } /// Unified state machine engine for all Intel 400x components /// /// This struct provides a unified state machine implementation that can be used /// by all Intel 400x series components. It replaces individual chip state machines /// with a single, extensible framework that handles common operations while /// allowing component-specific behavior through the Intel400xComponentBehavior trait. /// /// # Architecture Benefits /// /// The unified state machine approach provides several advantages over individual /// chip state machines: /// /// 1. **Code Reuse**: Common state transition logic is shared across all components /// 2. **Consistency**: All components follow the same timing and state patterns /// 3. **Maintainability**: Changes to common behavior only need to be made in one place /// 4. **Extensibility**: New chip types can be added without modifying the core state machine /// 5. **Testability**: The state machine can be tested independently of specific components /// /// # State Machine Operation /// /// The state machine operates by: /// /// 1. Receiving events from the component or external sources /// 2. Determining the next state based on current state and event /// 3. Calling appropriate state entry/exit methods on the component behavior /// 4. Executing state-specific logic through the component behavior /// /// # Example /// /// ```rust /// // Create a state machine for an Intel 4001 ROM chip /// let mut state_machine = Intel400xStateMachine::new( /// Intel400xComponentType::Intel4001, /// Duration::from_nanos(500) /// ); /// /// // Process a chip select event /// let new_state = process_event_direct( /// &mut state_machine, /// Intel400xEvent::ChipSelect /// ); /// /// assert_eq!(new_state, Intel400xUnifiedState::AddressPhase); /// ``` #[derive(Debug)] pub struct Intel400xStateMachine { /// The type of component this state machine is managing component_type: Intel400xComponentType, /// The current state of the state machine pub(crate) current_state: Intel400xUnifiedState, /// The previous state before the last transition previous_state: Intel400xUnifiedState, /// The high nibble (bits 7-4) of the currently latching address address_high_nibble: Option, /// The low nibble (bits 3-0) of the currently latching address address_low_nibble: Option, /// The time when the address was completely latched address_latch_time: Option, /// The access time to wait after address latching before data access access_time: Duration, } impl Intel400xStateMachine { /// Create a new state machine for the specified component type /// /// Initializes a new state machine instance with the given component type /// and access time. The state machine starts in the Idle state with all /// address fields cleared. /// /// # Parameters /// /// * `component_type` - The type of Intel 400x component being modeled /// * `access_time` - The time to wait after address latching before data access /// /// # Returns /// /// A new Intel400xStateMachine instance in the Idle state /// /// # Example /// /// ```rust /// // Create a state machine for an Intel 4002 RAM chip with 500ns access time /// let state_machine = Intel400xStateMachine::new( /// Intel400xComponentType::Intel4002, /// Duration::from_nanos(500) /// ); /// /// assert_eq!(state_machine.get_state(), Intel400xUnifiedState::Idle); /// assert_eq!(state_machine.get_component_type(), Intel400xComponentType::Intel4002); /// ``` pub fn new(component_type: Intel400xComponentType, access_time: Duration) -> Self { Self { component_type, current_state: Intel400xUnifiedState::Idle, previous_state: Intel400xUnifiedState::Idle, address_high_nibble: None, address_low_nibble: None, address_latch_time: None, access_time, } } /// Get the current state pub fn get_state(&self) -> Intel400xUnifiedState { self.current_state } /// Get the component type pub fn get_component_type(&self) -> Intel400xComponentType { self.component_type } /// Get the access time pub fn get_access_time(&self) -> Duration { self.access_time } /// Set the access time pub fn set_access_time(&mut self, access_time: Duration) { self.access_time = access_time; } /// Get address high nibble pub fn get_address_high_nibble(&self) -> Option { self.address_high_nibble } /// Set address high nibble pub fn set_address_high_nibble(&mut self, nibble: Option) { self.address_high_nibble = nibble; } /// Get address low nibble pub fn get_address_low_nibble(&self) -> Option { self.address_low_nibble } /// Set address low nibble pub fn set_address_low_nibble(&mut self, nibble: Option) { self.address_low_nibble = nibble; } /// Get address latch time pub fn get_address_latch_time(&self) -> Option { self.address_latch_time } /// Set address latch time pub fn set_address_latch_time(&mut self, time: Option) { self.address_latch_time = time; } /// Check if full address is ready pub fn is_full_address_ready(&self) -> bool { self.address_high_nibble.is_some() && self.address_low_nibble.is_some() } /// Get the full address if ready pub fn get_full_address(&self) -> Option { if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) { Some(((high as u16) << 4) | (low as u16)) } else { None } } /// Reset the state machine to initial state pub fn reset(&mut self) { self.previous_state = self.current_state; self.current_state = Intel400xUnifiedState::Idle; self.address_high_nibble = None; self.address_low_nibble = None; self.address_latch_time = None; } /// Handle address nibble latching /// Returns true only when BOTH nibbles have been latched (address complete) pub fn handle_address_nibble(&mut self, nibble: u8) -> bool { if self.address_high_nibble.is_none() { // First cycle: latch high nibble (bits 7-4) self.address_high_nibble = Some(nibble); false // Address not complete yet - still need low nibble } else if self.address_low_nibble.is_none() { // Second cycle: latch low nibble (bits 3-0) and transition to latency wait self.address_low_nibble = Some(nibble); self.address_latch_time = Some(Instant::now()); true // Address complete - both nibbles latched } else { // Address already latched, ignore false } } /// Check if latency has elapsed pub fn is_latency_elapsed(&self) -> bool { if let Some(latch_time) = self.address_latch_time { latch_time.elapsed() >= self.access_time } else { false } } /// Determine the next state based on current state and event pub fn determine_next_state( &self, event: Intel400xEvent, behavior: &dyn Intel400xComponentBehavior, ) -> Intel400xUnifiedState { self.determine_next_state_generic(event, behavior) } /// Determine the next state based on current state and event fn determine_next_state_generic( &self, event: Intel400xEvent, behavior: &T, ) -> Intel400xUnifiedState { match (self.current_state, event) { // Common state transitions (Intel400xUnifiedState::Idle, Intel400xEvent::ChipSelect) => { if behavior.responds_to_address() { Intel400xUnifiedState::AddressPhase } else { // Intel 4003 ignores address transmission and goes to ShiftData match behavior.get_component_type() { Intel400xComponentType::Intel4003 => Intel400xUnifiedState::ShiftData, _ => Intel400xUnifiedState::AddressPhase, } } } (Intel400xUnifiedState::Idle, Intel400xEvent::ResetActive) => { Intel400xUnifiedState::Idle } (Intel400xUnifiedState::AddressPhase, Intel400xEvent::AddressComplete) => { Intel400xUnifiedState::WaitLatency } (Intel400xUnifiedState::WaitLatency, Intel400xEvent::LatencyElapsed) => { match behavior.get_component_type() { Intel400xComponentType::Intel4002 => Intel400xUnifiedState::ReadData, // RAM reads data Intel400xComponentType::Intel4003 => Intel400xUnifiedState::ShiftData, _ => Intel400xUnifiedState::DriveData, // Default to DriveData for consistency } } (Intel400xUnifiedState::DriveData, Intel400xEvent::OperationComplete) => { Intel400xUnifiedState::Idle } (Intel400xUnifiedState::ReadData, Intel400xEvent::OperationComplete) => { Intel400xUnifiedState::Idle } (Intel400xUnifiedState::WriteData, Intel400xEvent::OperationComplete) => { Intel400xUnifiedState::Idle } (Intel400xUnifiedState::OutputPort, Intel400xEvent::OperationComplete) => { Intel400xUnifiedState::Idle } // Intel 4004 (CPU) specific transitions (Intel400xUnifiedState::FetchInstruction, Intel400xEvent::ClockPhi2Rising) => { Intel400xUnifiedState::ExecuteInstruction } (Intel400xUnifiedState::ExecuteInstruction, Intel400xEvent::ClockPhi1Rising) => { Intel400xUnifiedState::FetchInstruction } // Reset handling for all states (_, Intel400xEvent::ResetActive) => Intel400xUnifiedState::Idle, // Default: stay in current state _ => self.current_state, } } /// Execute the current state's logic pub fn execute_current_state(&mut self, behavior: &mut dyn Intel400xComponentBehavior) { behavior.execute_state_logic(self.current_state); } } // No-op behavior implementation for process_event_direct struct NoOpBehavior { component_type: Intel400xComponentType, } impl NoOpBehavior { fn new(component_type: Intel400xComponentType) -> Self { Self { component_type } } } impl Intel400xComponentBehavior for NoOpBehavior { fn get_component_type(&self) -> Intel400xComponentType { self.component_type } fn on_state_entry(&mut self, _state: Intel400xUnifiedState) { // No-op } fn on_state_exit(&mut self, _state: Intel400xUnifiedState) { // No-op } fn handle_component_event(&mut self, _event: Intel400xEvent) -> bool { false // Let state machine handle all events } fn execute_state_logic(&mut self, _state: Intel400xUnifiedState) { // No-op } fn responds_to_address(&self) -> bool { match self.get_component_type() { Intel400xComponentType::Intel4003 => false, _ => true, } } } /// Process an event and update state if needed /// /// This function is the main entry point for processing events in the unified /// state machine. It handles component-specific event processing first, then /// determines if a state transition is needed, and finally executes the /// appropriate state entry/exit logic. /// /// # Parameters /// /// * `state_machine` - The state machine to update /// * `event` - The event to process /// * `behavior` - The component behavior implementation /// /// # Returns /// /// `true` if the event was handled (either by the component or by a state /// transition), `false` if the event had no effect. /// /// # Example /// /// ```rust /// let mut state_machine = Intel400xStateMachine::new( /// Intel400xComponentType::Intel4001, /// Duration::from_nanos(500) /// ); /// let mut component = Intel4001Component::new(); /// /// // Process a chip select event /// let handled = process_event( /// &mut state_machine, /// Intel400xEvent::ChipSelect, /// &mut component /// ); /// /// assert!(handled); /// assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase); /// ``` pub fn process_event( state_machine: &mut Intel400xStateMachine, event: Intel400xEvent, behavior: &mut T, ) -> bool { // Handle component-specific event processing first if behavior.handle_component_event(event) { return true; // Event was handled by component } // Process state transitions based on current state and event let new_state = state_machine.determine_next_state_generic(event, behavior); if new_state != state_machine.current_state { // Exit current state behavior.on_state_exit(state_machine.current_state); // Update state state_machine.previous_state = state_machine.current_state; state_machine.current_state = new_state; // Enter new state behavior.on_state_entry(state_machine.current_state); return true; // State transition occurred } false // No state transition } /// Process an event without requiring a mutable reference to a behavior /// /// This is a convenience method to avoid borrowing conflicts when a mutable /// reference to the component behavior is not available. It uses a no-op /// behavior implementation to determine state transitions without calling /// the component's behavior methods. /// /// # Parameters /// /// * `state_machine` - The state machine to update /// * `event` - The event to process /// /// # Returns /// /// The new state after processing the event /// /// # Note /// /// This method only updates the state machine's internal state. It does not /// call the component's behavior methods (on_state_entry, on_state_exit, etc.). /// Use `process_event` when full behavior processing is needed. /// /// # Example /// /// ```rust /// let mut state_machine = Intel400xStateMachine::new( /// Intel400xComponentType::Intel4001, /// Duration::from_nanos(500) /// ); /// /// // Process a chip select event without a behavior reference /// let new_state = process_event_direct( /// &mut state_machine, /// Intel400xEvent::ChipSelect /// ); /// /// assert_eq!(new_state, Intel400xUnifiedState::AddressPhase); /// ``` pub fn process_event_direct( state_machine: &mut Intel400xStateMachine, event: Intel400xEvent, ) -> Intel400xUnifiedState { // Use a simple state transition logic without calling behavior methods let no_op_behavior = NoOpBehavior::new(state_machine.get_component_type()); let new_state = state_machine.determine_next_state_generic(event, &no_op_behavior); // Update the state machine's current state state_machine.previous_state = state_machine.current_state; state_machine.current_state = new_state; new_state } // Conversion implementations for compatibility with existing code impl From for Intel400xUnifiedState { fn from(state: MemoryState) -> Self { match state { MemoryState::Idle => Intel400xUnifiedState::Idle, MemoryState::AddressPhase => Intel400xUnifiedState::AddressPhase, MemoryState::WaitLatency => Intel400xUnifiedState::WaitLatency, MemoryState::DriveData => Intel400xUnifiedState::DriveData, } } } impl From for MemoryState { fn from(state: Intel400xUnifiedState) -> Self { match state { Intel400xUnifiedState::Idle => MemoryState::Idle, Intel400xUnifiedState::AddressPhase => MemoryState::AddressPhase, Intel400xUnifiedState::WaitLatency => MemoryState::WaitLatency, Intel400xUnifiedState::DriveData => MemoryState::DriveData, _ => MemoryState::Idle, // Default to Idle for other states } } } // Legacy RamState enum for compatibility with existing code #[derive(Debug, Clone, Copy, PartialEq)] pub enum RamState { Idle, AddressPhase, WaitLatency, ReadData, WriteData, OutputPort, } // Type alias for legacy TimingState to maintain compatibility pub type TimingState = MemoryState; // Conversion implementations for compatibility with existing code impl From for MemoryState { fn from(state: RamState) -> Self { match state { RamState::Idle => MemoryState::Idle, RamState::AddressPhase => MemoryState::AddressPhase, RamState::WaitLatency => MemoryState::WaitLatency, RamState::ReadData => MemoryState::DriveData, RamState::WriteData => MemoryState::DriveData, RamState::OutputPort => MemoryState::DriveData, } } } impl MemoryState { pub fn is_idle(&self) -> bool { matches!(self, MemoryState::Idle) } pub fn is_address_phase(&self) -> bool { matches!(self, MemoryState::AddressPhase) } pub fn is_waiting_latency(&self) -> bool { matches!(self, MemoryState::WaitLatency) } pub fn is_driving_data(&self) -> bool { matches!(self, MemoryState::DriveData) } } // Utility functions for common operations pub mod utils { use super::*; /// Create a unique driver name for a component pub fn create_driver_name(component_name: &str, suffix: &str) -> String { format!("{}_{}", component_name, suffix) } /// Check if a pin value represents a logical high pub fn is_pin_high(pin: &Arc>) -> bool { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { false } } /// Check if a pin value represents high impedance pub fn is_pin_high_z(pin: &Arc>) -> bool { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::HighZ } else { false } } /// Safely read a pin value with fallback pub fn read_pin_safe(pin: &Arc>, default: PinValue) -> PinValue { if let Ok(pin_guard) = pin.lock() { pin_guard.read() } else { default } } /// Safely set a pin driver with error handling pub fn set_pin_driver_safe(pin: &Arc>, driver_name: String, value: PinValue) { if let Ok(mut pin_guard) = pin.lock() { pin_guard.set_driver(Some(driver_name), value); } } } // Re-export commonly used items for convenience pub use utils::*; #[cfg(test)] mod tests { use super::*; // Mock component behavior for testing struct MockComponentBehavior { component_type: Intel400xComponentType, event_handled: bool, state_entry_called: bool, state_exit_called: bool, execute_logic_called: bool, } impl MockComponentBehavior { fn new(component_type: Intel400xComponentType) -> Self { Self { component_type, event_handled: false, state_entry_called: false, state_exit_called: false, execute_logic_called: false, } } fn reset(&mut self) { self.event_handled = false; self.state_entry_called = false; self.state_exit_called = false; self.execute_logic_called = false; } } impl Intel400xComponentBehavior for MockComponentBehavior { fn get_component_type(&self) -> Intel400xComponentType { self.component_type } fn on_state_entry(&mut self, _state: Intel400xUnifiedState) { self.state_entry_called = true; } fn on_state_exit(&mut self, _state: Intel400xUnifiedState) { self.state_exit_called = true; } fn handle_component_event(&mut self, _event: Intel400xEvent) -> bool { self.event_handled = true; false // Return false to allow state machine to handle the event } fn execute_state_logic(&mut self, _state: Intel400xUnifiedState) { self.execute_logic_called = true; } fn responds_to_address(&self) -> bool { match self.component_type { Intel400xComponentType::Intel4003 => false, _ => true, } } } #[test] fn test_state_machine_creation() { let sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4001, Duration::from_nanos(500), ); assert_eq!(sm.get_component_type(), Intel400xComponentType::Intel4001); assert_eq!(sm.get_state(), Intel400xUnifiedState::Idle); assert_eq!(sm.get_access_time(), Duration::from_nanos(500)); assert!(!sm.is_full_address_ready()); assert_eq!(sm.get_full_address(), None); assert_eq!(sm.get_address_high_nibble(), None); assert_eq!(sm.get_address_low_nibble(), None); assert_eq!(sm.get_address_latch_time(), None); } #[test] fn test_address_latching() { let mut sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4001, Duration::from_nanos(500), ); // First nibble should latch high nibble but NOT complete the address // Returns false because we still need the low nibble assert!(!sm.handle_address_nibble(0xA)); assert_eq!(sm.get_address_high_nibble(), Some(0xA)); assert_eq!(sm.get_address_low_nibble(), None); assert!(!sm.is_full_address_ready()); // Second nibble should latch low nibble and complete address // Returns true because address is now complete assert!(sm.handle_address_nibble(0x5)); assert_eq!(sm.get_address_high_nibble(), Some(0xA)); assert_eq!(sm.get_address_low_nibble(), Some(0x5)); assert!(sm.is_full_address_ready()); assert_eq!(sm.get_full_address(), Some(0xA5)); assert!(sm.get_address_latch_time().is_some()); // Address already latched should not change anything assert!(!sm.handle_address_nibble(0x3)); assert_eq!(sm.get_address_high_nibble(), Some(0xA)); assert_eq!(sm.get_address_low_nibble(), Some(0x5)); assert!(sm.is_full_address_ready()); assert_eq!(sm.get_full_address(), Some(0xA5)); assert!(sm.get_address_latch_time().is_some()); } #[test] fn test_state_transitions() { let mut sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4001, Duration::from_nanos(500), ); let mut behavior = MockComponentBehavior::new(Intel400xComponentType::Intel4001); // Test all state transitions let states = vec![ Intel400xUnifiedState::Idle, Intel400xUnifiedState::AddressPhase, Intel400xUnifiedState::WaitLatency, Intel400xUnifiedState::DriveData, ]; for &_state in &states { sm.reset(); assert_eq!(sm.get_state(), Intel400xUnifiedState::Idle); process_event_direct(&mut sm, Intel400xEvent::ChipSelect); assert_eq!(sm.get_state(), Intel400xUnifiedState::AddressPhase); // Note: behavior.state_entry_called is not called with process_event_direct // assert!(behavior.state_entry_called); behavior.reset(); process_event_direct(&mut sm, Intel400xEvent::AddressComplete); assert_eq!(sm.get_state(), Intel400xUnifiedState::WaitLatency); // Note: behavior.state_entry_called is not called with process_event_direct behavior.reset(); process_event_direct(&mut sm, Intel400xEvent::LatencyElapsed); assert_eq!(sm.get_state(), Intel400xUnifiedState::DriveData); // Note: behavior.state_entry_called is not called with process_event_direct behavior.reset(); process_event_direct(&mut sm, Intel400xEvent::OperationComplete); assert_eq!(sm.get_state(), Intel400xUnifiedState::Idle); // Note: behavior.state_exit_called is not called with process_event_direct behavior.reset(); } } #[test] fn test_intel4003_ignores_address() { let mut sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4003, Duration::from_nanos(200), ); let mut behavior = MockComponentBehavior::new(Intel400xComponentType::Intel4003); // Intel 4003 should not respond to address transmission assert!(!behavior.responds_to_address()); // Chip select should transition to ShiftData instead of AddressPhase process_event_direct(&mut sm, Intel400xEvent::ChipSelect); assert_eq!(sm.get_state(), Intel400xUnifiedState::ShiftData); // Note: behavior.state_entry_called is not called with process_event_direct // assert!(behavior.state_entry_called); behavior.reset(); } #[test] fn test_reset_handling() { let mut sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4002, Duration::from_nanos(500), ); let mut behavior = MockComponentBehavior::new(Intel400xComponentType::Intel4002); // Set up a non-idle state process_event_direct(&mut sm, Intel400xEvent::ChipSelect); assert_eq!(sm.get_state(), Intel400xUnifiedState::AddressPhase); // Note: behavior.state_entry_called is not called with process_event_direct behavior.reset(); // Reset should return to Idle state process_event_direct(&mut sm, Intel400xEvent::ResetActive); assert_eq!(sm.get_state(), Intel400xUnifiedState::Idle); // Note: behavior.state_exit_called is not called with process_event_direct behavior.reset(); } #[test] fn test_latency_timing() { let mut sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4001, Duration::from_millis(1), // Use 1ms for test reliability ); let mut behavior = MockComponentBehavior::new(Intel400xComponentType::Intel4001); // Initially no latency should be elapsed assert!(!sm.is_latency_elapsed()); // Latch an address sm.handle_address_nibble(0xA); sm.handle_address_nibble(0x5); // Initially latency should not be elapsed (need to wait for access time) assert!(!sm.is_latency_elapsed()); // Wait for latency to elapse std::thread::sleep(Duration::from_millis(2)); assert!(sm.is_latency_elapsed()); // First need to transition to AddressPhase state process_event_direct(&mut sm, Intel400xEvent::ChipSelect); assert_eq!(sm.get_state(), Intel400xUnifiedState::AddressPhase); // Now transition to WaitLatency state process_event_direct(&mut sm, Intel400xEvent::AddressComplete); assert_eq!(sm.get_state(), Intel400xUnifiedState::WaitLatency); // Wait for latency to elapse std::thread::sleep(Duration::from_millis(2)); assert!(sm.is_latency_elapsed()); // Latency elapsed should transition to DriveData println!( "DEBUG: Before LatencyElapsed event, state: {:?}", sm.get_state() ); // Ensure state machine is in WaitLatency state before processing LatencyElapsed assert_eq!(sm.get_state(), Intel400xUnifiedState::WaitLatency); process_event_direct(&mut sm, Intel400xEvent::LatencyElapsed); println!( "DEBUG: After LatencyElapsed event, state: {:?}", sm.get_state() ); assert_eq!(sm.get_state(), Intel400xUnifiedState::DriveData); // Note: behavior.state_entry_called is not called with process_event_direct // assert!(behavior.state_entry_called); behavior.reset(); } #[test] fn test_state_execution() { let mut sm = Intel400xStateMachine::new( Intel400xComponentType::Intel4001, Duration::from_nanos(500), ); let mut behavior = MockComponentBehavior::new(Intel400xComponentType::Intel4001); // Execute current state logic sm.execute_current_state(&mut behavior); assert!(behavior.execute_logic_called); } #[test] fn test_component_type_specific_states() { // Test Intel 4002 specific states let mut sm4002 = Intel400xStateMachine::new( Intel400xComponentType::Intel4002, Duration::from_nanos(500), ); let mut behavior4002 = MockComponentBehavior::new(Intel400xComponentType::Intel4002); process_event_direct(&mut sm4002, Intel400xEvent::ChipSelect); process_event_direct(&mut sm4002, Intel400xEvent::ChipSelect); process_event_direct(&mut sm4002, Intel400xEvent::AddressComplete); process_event_direct(&mut sm4002, Intel400xEvent::LatencyElapsed); assert_eq!(sm4002.get_state(), Intel400xUnifiedState::ReadData); // Note: behavior4002.state_entry_called is not called with process_event_direct behavior4002.reset(); // Test Intel 4003 specific states let mut sm4003 = Intel400xStateMachine::new( Intel400xComponentType::Intel4003, Duration::from_nanos(200), ); let mut behavior4003 = MockComponentBehavior::new(Intel400xComponentType::Intel4003); process_event_direct(&mut sm4003, Intel400xEvent::ChipSelect); process_event_direct(&mut sm4003, Intel400xEvent::ChipSelect); process_event_direct(&mut sm4003, Intel400xEvent::LatencyElapsed); assert_eq!(sm4003.get_state(), Intel400xUnifiedState::ShiftData); // Note: behavior4003.state_entry_called is not called with process_event_direct // assert!(behavior4003.state_entry_called); behavior4003.reset(); } }