use parking_lot::Mutex; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use std::thread; use std::time::Duration; use crate::component::{BaseComponent, Component, RunnableComponent}; use crate::component_timing::ComponentTiming; use crate::components::common::intel_400x::{ process_event_direct, Intel400xAddressHandling, Intel400xClockHandling, Intel400xComponentBehavior, Intel400xComponentType, Intel400xControlPins, Intel400xDataBus, Intel400xEvent, Intel400xResetHandling, Intel400xStateMachine, Intel400xTimingState, Intel400xUnifiedState, TimingState, }; use crate::pin::{Pin, PinValue}; use crate::virtual_clock::VirtualClock; /// Intel 4003 - 10-bit serial-in, serial-out shift register /// Part of the MCS-4 family, designed to work with Intel 4004 CPU /// Features 10-bit shift register with serial input/output capability /// /// Hardware Deviations: /// - Timing simplified for testing (real hardware has specific clock requirements) /// - I/O operations may have different timing than real hardware pub struct Intel4003 { pub base: BaseComponent, shift_register: u16, // 10-bit shift register (bits 0-9 used) input_buffer: u8, // 8-bit input buffer for serial data output_buffer: u8, // 8-bit output buffer for serial data access_time: Duration, // Shift register access latency (200ns) serial_mode: SerialMode, // Current serial mode configuration // Unified state machine implementation state_machine: Intel400xStateMachine, // Unified state machine for Intel 400x components // Clock edge detection prev_phi1: PinValue, // Previous Φ1 clock state for edge detection prev_phi2: PinValue, // Previous Φ2 clock state for edge detection // Virtual time support (Phase 6) virtual_clock: Option>, // Injected by ConfigurableSystem (Phase 7) /// Virtual time snapshot for the current update cycle. /// Set at the start of `update_at_time()` so all internal methods use a /// consistent time value instead of reading `VirtualClock::now()` which /// may have advanced since the cycle started. current_update_time: Duration, } /// Serial operation modes for 4003 shift register #[derive(Debug, Clone, Copy, PartialEq)] pub enum SerialMode { Shift, // Shift mode (data shifts through register) Load, // Load mode (data loaded into register) Hold, // Hold mode (register holds current value) } impl Intel400xClockHandling for Intel4003 { fn get_base(&self) -> &BaseComponent { &self.base } } impl Intel400xDataBus for Intel4003 { fn get_base(&self) -> &BaseComponent { &self.base } } impl Intel400xAddressHandling for Intel4003 { fn get_base(&self) -> &BaseComponent { &self.base } } impl Intel400xControlPins for Intel4003 { fn get_base(&self) -> &BaseComponent { &self.base } } impl Intel400xResetHandling for Intel4003 { fn get_base(&self) -> &BaseComponent { &self.base } fn perform_reset(&mut self) { // Reset operations specific to Intel4003 // Note: This is called from handle_reset, so we don't need to check reset pin again self.state_machine.reset(); self.tri_state_data_bus(); // Clear shift register state self.shift_register = 0; self.input_buffer = 0; self.output_buffer = 0; self.serial_mode = SerialMode::Hold; // Reset clock edge detection state to ensure proper edge detection after reset // This is critical for tests that reset the chip between operations self.prev_phi1 = PinValue::Low; self.prev_phi2 = PinValue::Low; } } impl Intel400xTimingState for Intel4003 { fn get_timing_state(&self) -> TimingState { // Convert from Intel400xUnifiedState to TimingState match self.state_machine.get_state() { Intel400xUnifiedState::Idle => TimingState::Idle, Intel400xUnifiedState::AddressPhase => TimingState::AddressPhase, Intel400xUnifiedState::WaitLatency => TimingState::WaitLatency, Intel400xUnifiedState::DriveData => TimingState::DriveData, Intel400xUnifiedState::ShiftData => TimingState::DriveData, Intel400xUnifiedState::LoadData => TimingState::DriveData, // Handle all other states _ => TimingState::Idle, } } fn set_timing_state(&mut self, state: TimingState) { // Convert from TimingState to Intel400xUnifiedState let unified_state = match state { TimingState::Idle => Intel400xUnifiedState::Idle, TimingState::AddressPhase => Intel400xUnifiedState::AddressPhase, TimingState::WaitLatency => Intel400xUnifiedState::WaitLatency, TimingState::DriveData => Intel400xUnifiedState::ShiftData, // Default to ShiftData }; // Use state machine's reset method to transition to new state // This is a compatibility method for legacy state handling self.state_machine.reset(); self.state_machine.current_state = unified_state; } fn get_address_latch_time(&self) -> Option { self.state_machine.get_address_latch_time() } fn set_address_latch_time(&mut self, time: Option) { self.state_machine.set_address_latch_time(time); } fn get_full_address_ready(&self) -> bool { self.state_machine.is_full_address_ready() } fn set_full_address_ready(&mut self, _ready: bool) { // This is handled by state machine, but keep for compatibility } fn get_address_high_nibble(&self) -> Option { self.state_machine.get_address_high_nibble() } fn set_address_high_nibble(&mut self, nibble: Option) { self.state_machine.set_address_high_nibble(nibble); } fn get_address_low_nibble(&self) -> Option { self.state_machine.get_address_low_nibble() } fn set_address_low_nibble(&mut self, nibble: Option) { self.state_machine.set_address_low_nibble(nibble); } fn get_access_time(&self) -> Duration { self.state_machine.get_access_time() } fn is_latency_elapsed_at(&self, virtual_time: Duration) -> bool { self.state_machine.is_latency_elapsed_at(virtual_time) } } impl Intel400xComponentBehavior for Intel4003 { fn get_component_type(&self) -> Intel400xComponentType { Intel400xComponentType::Intel4003 } fn on_state_entry(&mut self, state: Intel400xUnifiedState) { match state { Intel400xUnifiedState::Idle => { // Entry actions for Idle state self.tri_state_data_bus(); } Intel400xUnifiedState::AddressPhase => { // Entry actions for AddressPhase state // Address latching will happen in execute_state_logic } Intel400xUnifiedState::WaitLatency => { // Entry actions for WaitLatency state // Address is already latched, start access timer } Intel400xUnifiedState::ShiftData => { // Entry actions for ShiftData state // Data shifting will happen in execute_state_logic } Intel400xUnifiedState::LoadData => { // Entry actions for LoadData state // Data loading will happen in execute_state_logic } // Handle all other states _ => {} } } fn on_state_exit(&mut self, state: Intel400xUnifiedState) { match state { Intel400xUnifiedState::ShiftData | Intel400xUnifiedState::LoadData => { // Exit actions for data states self.tri_state_data_bus(); } // Handle all other states _ => {} } } fn handle_component_event(&mut self, event: Intel400xEvent) -> bool { match event { Intel400xEvent::ClockPhi1Rising => { // Handle Φ1 rising edge self.handle_phi1_rising(); true // Event was handled } Intel400xEvent::ClockPhi1Falling => { // Handle Φ1 falling edge self.handle_phi1_falling(); true // Event was handled } Intel400xEvent::ClockPhi2Rising => { // Handle Φ2 rising edge self.handle_phi2_rising(); true // Event was handled } Intel400xEvent::ClockPhi2Falling => { // Handle Φ2 falling edge self.handle_phi2_falling(); true // Event was handled } Intel400xEvent::ResetActive => { // Handle reset self.perform_reset(); true // Event was handled } _ => false, // Let the generic state machine handle other events } } fn execute_state_logic(&mut self, state: Intel400xUnifiedState) { match state { Intel400xUnifiedState::Idle => { // In idle state, ensure bus is tri-stated self.tri_state_data_bus(); } Intel400xUnifiedState::AddressPhase => { // Handle address latching during address phase self.handle_address_latching(); } Intel400xUnifiedState::WaitLatency => { // Check if latency has elapsed using virtual time. // // **With VirtualClock (production):** `address_latch_time` was recorded // by `handle_address_nibble_at()` in virtual time. We check if enough // virtual time has elapsed since then (≥ 200ns access time). // // **Without VirtualClock (standalone tests):** `current_virtual_time()` // returns `Duration::ZERO`, so `is_latency_elapsed_at()` would always // return `false`. The `!has_clock` guard short-circuits to skip the wait. // // NOTE: The old `Instant::now()`-based code would naturally wait ~200ns. // This new code skips the wait in legacy mode, making standalone tests // less hardware-accurate but preventing them from hanging in WaitLatency. let has_clock = self.virtual_clock.is_some(); let vtime = self.current_update_time; if !has_clock || self.state_machine.is_latency_elapsed_at(vtime) { self.process_latency_elapsed(); } } Intel400xUnifiedState::ShiftData => { // Perform shift operation self.handle_shift_operation(); } Intel400xUnifiedState::LoadData => { // Perform load operation self.handle_load_operation(); } // Handle other states (not used by Intel 4003) _ => {} } } fn responds_to_address(&self) -> bool { // Intel 4003 mostly ignores address transmission false } } impl Intel4003 { /// Create a new Intel 4003 shift register with specified access time /// Parameters: name - Component identifier, access_time_ns - Access time in nanoseconds /// Returns: New Intel4003 instance with configurable access timing pub fn new(name: String) -> Self { Self::new_with_access_time(name, 200) // Default 200ns access time (faster than ROM/RAM) } /// Create a new Intel 4003 shift register with custom access time (for testing) /// Parameters: name - Component identifier, access_time_ns - Access time in nanoseconds /// Returns: New Intel4003 instance with configurable access timing pub fn new_with_access_time(name: String, access_time_ns: u64) -> Self { // Intel 4003 pinout (per datasheet): // - 4 data pins (D0-D3) for multiplexed address/data // - 4 I/O pins (IO0-IO3) // - Control pins: SYNC, RESET, CM, CI // - Clock pins: Φ1, Φ2 (two-phase clock from 4004 CPU) // // Control pin behavior: // - SYNC: Marks start of instruction cycle // - RESET: Clears internal state // - CM: Chip select (must be HIGH for shift register access) // - CI: I/O select (distinguishes I/O vs memory when chip selected) let pin_names = vec![ "D0", "D1", "D2", "D3", // Data/Address pins "IO0", "IO1", "IO2", "IO3", // I/O pins "SYNC", // Sync signal "CM", // CM-ROM: ROM/RAM Chip Select "CI", // CM-RAM: RAM Chip Select (I/O vs ROM access) "RESET", // Reset "PHI1", // Clock phase 1 "PHI2", // Clock phase 2 ]; let pins = BaseComponent::create_pin_map(&pin_names, &name); Intel4003 { base: BaseComponent::new(name, pins), shift_register: 0, input_buffer: 0, output_buffer: 0, access_time: Duration::from_nanos(access_time_ns), serial_mode: SerialMode::Hold, state_machine: Intel400xStateMachine::new( Intel400xComponentType::Intel4003, Duration::from_nanos(access_time_ns), ), prev_phi1: PinValue::Low, prev_phi2: PinValue::Low, virtual_clock: None, current_update_time: Duration::ZERO, } } /// Inject a VirtualClock for virtual time-based operation. /// When set, `update()` uses virtual time and `run()` uses `wait_until()`. /// This method is called by ConfigurableSystem in Phase 7. pub fn set_virtual_clock(&mut self, clock: Arc) { self.virtual_clock = Some(clock); } /// Get current virtual time, or Duration::ZERO if no VirtualClock is injected. /// /// # Zero-Time Invariant /// /// When no VirtualClock is injected, this returns `Duration::ZERO`. The `WaitLatency` /// handler checks `vtime == Duration::ZERO` to skip latency enforcement in legacy mode. /// This is intentional: legacy tests don't use VirtualClock and expect latency to be /// skipped for backward compatibility. fn current_virtual_time(&self) -> Duration { self.virtual_clock.as_ref().map_or(Duration::ZERO, |vc| vc.now()) } /// Set access time for simulation /// Parameters: access_time_ns - Access time in nanoseconds pub fn set_access_time(&mut self, access_time_ns: u64) { self.access_time = Duration::from_nanos(access_time_ns); self.state_machine .set_access_time(Duration::from_nanos(access_time_ns)); } /// Get current access time /// Returns: Access time in nanoseconds pub fn get_access_time(&self) -> u64 { self.access_time.as_nanos() as u64 } /// Tri-state data bus pins fn tri_state_data_bus(&self) { for i in 0..4 { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { let mut pin_guard = pin.lock(); pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ); } } } /// Handle Φ1 rising edge - Address and control phase /// Hardware: Φ1 high = CPU drives bus with address/control information /// Memory operations start when SYNC goes high during Φ1 rising edge /// Focus: Address latching, reset, I/O operations fn handle_phi1_rising(&mut self) { // Handle system reset first (highest priority) self.handle_reset("RESET"); // Check for shift register operation start on Φ1 rising edge with SYNC high // Hardware: Shift register operations start when SYNC goes high during Φ1 // Note: SYNC marks instruction fetch start, but exact logic involves CPU cycle state. // Shift register access: CM=1 (chip_select), CI=0 (!io_select) // I/O access: CM=1 (chip_select), CI=1 (io_select) let sync = self.read_sync_pin(); let chip_select = self.read_cm_rom_pin(); let io_select = self.read_ci_pin(); if sync && chip_select && !io_select { // Start shift register operation on Φ1 rising edge (shift register access) // Only start a new operation if we're currently idle if self.state_machine.get_state() == Intel400xUnifiedState::Idle { self.process_chip_select(); } } } /// Handle Φ1 falling edge - End of address phase /// Hardware: Φ1 low = End of CPU address driving phase fn handle_phi1_falling(&mut self) { // Intel 4003 ignores address transmission (responds_to_address() = false) // No address latching needed for shift register operations } /// Handle Φ2 rising edge - Data phase /// Hardware: Φ2 high = Peripherals drive bus with data /// Focus: Data driving operations fn handle_phi2_rising(&mut self) { // Execute current state logic self.execute_state_machine(); } /// Handle Φ2 falling edge - End of data phase /// Hardware: Φ2 low = End of data driving phase, time to tri-state bus /// Data is held on bus during Φ2 high, tri-stated when Φ2 falls /// Focus: Clean up and return to idle state fn handle_phi2_falling(&mut self) { match self.state_machine.get_state() { Intel400xUnifiedState::ShiftData | Intel400xUnifiedState::LoadData => { // End of data phase - tri-state bus and return to idle self.tri_state_data_bus(); self.process_operation_complete(); } Intel400xUnifiedState::Idle | Intel400xUnifiedState::AddressPhase | Intel400xUnifiedState::WaitLatency => { // For other states, just ensure bus is tri-stated self.tri_state_data_bus(); } // Handle all other states _ => { // Default behavior for unhandled states self.tri_state_data_bus(); } } } /// Handle address nibble latching during address phase /// Hardware: CPU provides 8-bit address as two 4-bit nibbles /// Note: Intel 4003 mostly ignores address transmission for shift operations fn handle_address_latching(&mut self) { // Intel 4003 mostly ignores address transmission for shift operations // But we still need to handle the address nibble for compatibility let nibble = self.read_data_bus() & 0x0F; // Only 4 bits are valid for address nibbles if self.state_machine.handle_address_nibble_at(nibble, self.current_update_time) { // Address is complete — virtual time was recorded atomically by handle_address_nibble_at() // Transition to latency wait self.process_address_complete(); } } /// Handle shift register shift operation /// Hardware: Shift register shifts data based on clock edges fn handle_shift_operation(&mut self) { // Read data from bus let data = self.read_data_bus() & 0x0F; // Only 4 bits are valid // Shift data into register self.shift_register = ((self.shift_register << 4) | (data as u16)) & 0x03FF; // Keep only 10 bits // Update output buffer with shifted-out bit self.output_buffer = ((self.shift_register >> 9) & 1) as u8; } /// Handle shift register load operation /// Hardware: Load data into shift register fn handle_load_operation(&mut self) { // Read data from bus let data = self.read_data_bus() & 0x0F; // Only 4 bits are valid // Load data into register self.shift_register = (self.shift_register & 0xFF00) | (data as u16); // Keep lower 8 bits // Update output buffer with loaded data self.output_buffer = data; } /// Helper function to process latency elapsed event fn process_latency_elapsed(&mut self) { let old_state = self.state_machine.get_state(); let event = Intel400xEvent::LatencyElapsed; let new_state = process_event_direct(&mut self.state_machine, event); if new_state != old_state { self.on_state_exit(old_state); self.on_state_entry(new_state); } } /// Helper function to process chip select event fn process_chip_select(&mut self) { let old_state = self.state_machine.get_state(); let event = Intel400xEvent::ChipSelect; let new_state = process_event_direct(&mut self.state_machine, event); if new_state != old_state { self.on_state_exit(old_state); self.on_state_entry(new_state); } } /// Helper function to process operation complete event fn process_operation_complete(&mut self) { let old_state = self.state_machine.get_state(); let event = Intel400xEvent::OperationComplete; let new_state = process_event_direct(&mut self.state_machine, event); if new_state != old_state { self.on_state_exit(old_state); self.on_state_entry(new_state); } } /// Helper function to process address complete event fn process_address_complete(&mut self) { let old_state = self.state_machine.get_state(); let event = Intel400xEvent::AddressComplete; let new_state = process_event_direct(&mut self.state_machine, event); if new_state != old_state { self.on_state_exit(old_state); self.on_state_entry(new_state); } } /// Helper function to execute unified state machine logic fn execute_state_machine(&mut self) { // Execute the current state logic using the unified state machine let current_state = self.state_machine.get_state(); self.execute_state_logic(current_state); } } impl Component for Intel4003 { fn name(&self) -> String { self.base.name() } fn pins(&self) -> HashMap>> { self.base.pins() } fn get_pin(&self, name: &str) -> Result>, String> { self.base.get_pin(name) } /// Main update cycle - delegates to update_at_time with current virtual time /// Hardware: Responds to Φ1 and Φ2 clock edges from CPU fn update(&mut self) { let vtime = self.current_virtual_time(); self.update_at_time(vtime); } /// Virtual-time-aware update cycle - handles clock edge detection and operation dispatch /// Hardware: Responds to Φ1 and Φ2 clock edges from CPU fn update_at_time(&mut self, virtual_time: Duration) { // Snapshot virtual_time for the entire update cycle so internal methods // use a consistent time instead of reading VirtualClock::now() which // may have advanced. self.current_update_time = virtual_time; if !self.is_running() { return; } // Check for reset first (highest priority, independent of clock edges) // This matches Intel 4001 behavior where reset is processed immediately let reset_active = self.read_reset_pin(); if reset_active { self.perform_reset(); // Skip further processing if reset is active return; } // Handle both rising and falling edges for proper two-phase operation let phi1_rising = self.is_phi1_rising_edge(self.prev_phi1); let phi1_falling = self.is_phi1_falling_edge(self.prev_phi1); let phi2_rising = self.is_phi2_rising_edge(self.prev_phi2); let phi2_falling = self.is_phi2_falling_edge(self.prev_phi2); // Update clock states for next edge detection let (phi1, phi2) = self.read_clock_pins(); self.prev_phi1 = phi1; self.prev_phi2 = phi2; // Collect all events to process let mut events = Vec::new(); if phi1_rising { events.push(Intel400xEvent::ClockPhi1Rising); } if phi1_falling { events.push(Intel400xEvent::ClockPhi1Falling); } if phi2_rising { events.push(Intel400xEvent::ClockPhi2Rising); } if phi2_falling { events.push(Intel400xEvent::ClockPhi2Falling); } // Process all events for event in events { let old_state = self.state_machine.get_state(); let new_state = process_event_direct(&mut self.state_machine, event); if new_state != old_state { self.on_state_exit(old_state); self.on_state_entry(new_state); } } // Execute current state logic self.execute_state_machine(); } /// Run component with VirtualClock support or legacy fallback /// Hardware: Simulates continuous operation with clock edge detection fn run(&mut self) { self.base.set_running(true); // Initialize clock states for edge detection let (phi1, phi2) = self.read_clock_pins(); self.prev_phi1 = phi1; self.prev_phi2 = phi2; // If no VirtualClock is injected, fall back to wall-clock run loop let vclock = match self.virtual_clock.as_ref() { Some(vc) => Arc::clone(vc), None => { // Legacy mode: use wall-clock timing while self.is_running() { self.update(); thread::sleep(Duration::from_micros(1)); } return; } }; // Virtual time mode: wait for next tick to synchronize with clock while self.is_running() { let next_tick = vclock.now() + vclock.tick_size(); if let Some(current_vtime) = vclock.wait_until(next_tick) { self.update_at_time(current_vtime); } else { // Clock was interrupted, exit break; } } } /// Get timing constraints for Intel 4003 shift register /// Returns: ComponentTiming with 200ns access time, 100ns propagation delay fn timing(&self) -> ComponentTiming { ComponentTiming::intel_4003() } /// Stop component and tri-state all outputs /// Hardware: Component enters low-power state, all pins high-impedance fn stop(&mut self) { self.base.set_running(false); // Tri-state all outputs when stopped self.tri_state_data_bus(); } fn is_running(&self) -> bool { self.base.is_running() } fn set_running(&mut self, running: bool) { self.base.set_running(running); } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn set_virtual_clock_optional(&mut self, clock: Option>) { self.virtual_clock = clock; } fn has_virtual_clock(&self) -> bool { self.virtual_clock.is_some() } } impl RunnableComponent for Intel4003 {} // Intel 4003 specific methods impl Intel4003 { /// Get shift register value /// Returns: Current 10-bit shift register value pub fn get_shift_register(&self) -> u16 { self.shift_register } /// Set shift register value (for testing/debugging) /// Parameters: value - 10-bit value to set pub fn set_shift_register(&mut self, value: u16) { self.shift_register = value & 0x03FF; // Keep only 10 bits } /// Get input buffer value /// Returns: Current 8-bit input buffer value pub fn get_input_buffer(&self) -> u8 { self.input_buffer } /// Set input buffer value (for testing/debugging) /// Parameters: value - 8-bit value to set pub fn set_input_buffer(&mut self, value: u8) { self.input_buffer = value; } /// Get output buffer value /// Returns: Current 8-bit output buffer value pub fn get_output_buffer(&self) -> u8 { self.output_buffer } /// Set output buffer value (for testing/debugging) /// Parameters: value - 8-bit value to set pub fn set_output_buffer(&mut self, value: u8) { self.output_buffer = value; } /// Get serial mode /// Returns: Current serial mode pub fn get_serial_mode(&self) -> SerialMode { self.serial_mode } /// Set serial mode (for testing/debugging) /// Parameters: mode - Serial mode to set pub fn set_serial_mode(&mut self, mode: SerialMode) { self.serial_mode = mode; } /// Debug function to log state transitions for troubleshooting /// Parameters: test_name - Name of the test for context pub fn debug_state_transitions(&self, test_name: &str) { println!( "{} - State: {:?}, Register: 0x{:03X}, Input: 0x{:X}, Output: 0x{:X}", test_name, self.state_machine.get_state(), self.shift_register, self.input_buffer, self.output_buffer ); } } // Custom formatter for debugging impl std::fmt::Display for SerialMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SerialMode::Shift => write!(f, "Shift"), SerialMode::Load => write!(f, "Load"), SerialMode::Hold => write!(f, "Hold"), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_intel4003_creation() { let shift_reg = Intel4003::new("SHIFT_4003".to_string()); assert_eq!(shift_reg.name(), "SHIFT_4003"); assert_eq!(shift_reg.get_access_time(), 200); // Default 200ns assert!(!shift_reg.is_running()); } #[test] fn test_intel4003_shift_operations() { let mut shift_reg = Intel4003::new("SHIFT_4003".to_string()); // Test shift register operations shift_reg.set_shift_register(0x000); assert_eq!(shift_reg.get_shift_register(), 0x000); // Test shifting in data - handle_shift_operation reads from data bus // Since data bus is in HighZ state, it will read 0 shift_reg.set_shift_register(0x001); shift_reg.handle_shift_operation(); assert_eq!(shift_reg.get_shift_register(), 0x010); // 0x001 << 4 | 0 = 0x010 // Test multiple shifts - all will shift in 0 due to HighZ data bus shift_reg.handle_shift_operation(); // Shift in 0x0 assert_eq!(shift_reg.get_shift_register(), 0x100); // 0x010 << 4 | 0 = 0x100 shift_reg.handle_shift_operation(); // Shift in 0x0 assert_eq!(shift_reg.get_shift_register(), 0x000); // 0x100 << 4 | 0 = 0x1000, masked to 10 bits = 0x000 shift_reg.handle_shift_operation(); // Shift in 0x0 assert_eq!(shift_reg.get_shift_register(), 0x000); // 0x000 << 4 | 0 = 0x000 } #[test] fn test_intel4003_load_operations() { let mut shift_reg = Intel4003::new("SHIFT_4003".to_string()); // Test load operations shift_reg.set_shift_register(0x000); assert_eq!(shift_reg.get_shift_register(), 0x000); // Load data - handle_load_operation reads from data bus // Since data bus is in HighZ state, it will read 0 shift_reg.handle_load_operation(); assert_eq!(shift_reg.get_shift_register(), 0x000); // (0x000 & 0xFF00) | 0 = 0x000 assert_eq!(shift_reg.get_output_buffer(), 0x00); // Load data again - still 0 due to HighZ data bus shift_reg.handle_load_operation(); assert_eq!(shift_reg.get_shift_register(), 0x000); // (0x000 & 0xFF00) | 0 = 0x000 assert_eq!(shift_reg.get_output_buffer(), 0x00); } #[test] fn test_intel4003_serial_modes() { let mut shift_reg = Intel4003::new("MODE_4003".to_string()); // Test initial mode assert_eq!(shift_reg.get_serial_mode(), SerialMode::Hold); // Test setting modes shift_reg.set_serial_mode(SerialMode::Shift); assert_eq!(shift_reg.get_serial_mode(), SerialMode::Shift); shift_reg.set_serial_mode(SerialMode::Load); assert_eq!(shift_reg.get_serial_mode(), SerialMode::Load); shift_reg.set_serial_mode(SerialMode::Hold); assert_eq!(shift_reg.get_serial_mode(), SerialMode::Hold); } #[test] fn test_intel4003_buffers() { let mut shift_reg = Intel4003::new("BUFFER_4003".to_string()); // Test buffer operations assert_eq!(shift_reg.get_input_buffer(), 0); assert_eq!(shift_reg.get_output_buffer(), 0); shift_reg.set_input_buffer(0x12); assert_eq!(shift_reg.get_input_buffer(), 0x12); shift_reg.set_output_buffer(0x34); assert_eq!(shift_reg.get_output_buffer(), 0x34); } #[test] fn test_intel4003_configurable_access_time() { let mut shift_reg = Intel4003::new("TIME_4003".to_string()); // Test default access time assert_eq!(shift_reg.get_access_time(), 200); // Test setting custom access time shift_reg.set_access_time(100); assert_eq!(shift_reg.get_access_time(), 100); // Test constructor with custom access time let fast_shift = Intel4003::new_with_access_time("FAST_SHIFT".to_string(), 1); assert_eq!(fast_shift.get_access_time(), 1); assert_eq!(fast_shift.name(), "FAST_SHIFT"); } #[test] fn test_intel4003_state_transitions() { let mut shift_reg = Intel4003::new_with_access_time("StateTestSHIFT".to_string(), 1); // Test all state transitions let states = vec![ TimingState::Idle, TimingState::AddressPhase, TimingState::WaitLatency, TimingState::DriveData, ]; for &state in &states { shift_reg.set_timing_state(state); assert_eq!(shift_reg.get_timing_state(), state); } // Test return to idle shift_reg.set_timing_state(TimingState::Idle); assert_eq!(shift_reg.get_timing_state(), TimingState::Idle); } #[test] fn test_intel4003_address_handling() { let mut shift_reg = Intel4003::new_with_access_time("AddressTestSHIFT".to_string(), 1); // Test address nibble handling shift_reg.set_address_high_nibble(Some(0x12)); shift_reg.set_address_low_nibble(Some(0x34)); shift_reg.set_full_address_ready(true); assert_eq!(shift_reg.get_address_high_nibble(), Some(0x12)); assert_eq!(shift_reg.get_address_low_nibble(), Some(0x34)); assert_eq!(shift_reg.state_machine.is_full_address_ready(), true); // Test clearing shift_reg.set_address_high_nibble(None); shift_reg.set_address_low_nibble(None); shift_reg.set_full_address_ready(false); assert_eq!(shift_reg.get_address_high_nibble(), None); assert_eq!(shift_reg.get_address_low_nibble(), None); assert_eq!(shift_reg.state_machine.is_full_address_ready(), false); } #[test] fn test_intel4003_reset_behavior() { let mut shift_reg = Intel4003::new("ResetTestSHIFT".to_string()); // Set up some state first shift_reg.set_shift_register(0x123); shift_reg.set_input_buffer(0x45); shift_reg.set_output_buffer(0x67); shift_reg.set_serial_mode(SerialMode::Shift); // Verify state is set assert_eq!(shift_reg.get_shift_register(), 0x123); assert_eq!(shift_reg.get_input_buffer(), 0x45); assert_eq!(shift_reg.get_output_buffer(), 0x67); assert_eq!(shift_reg.get_serial_mode(), SerialMode::Shift); // Perform reset shift_reg.perform_reset(); // Verify all state is cleared assert_eq!(shift_reg.get_shift_register(), 0); assert_eq!(shift_reg.get_input_buffer(), 0); assert_eq!(shift_reg.get_output_buffer(), 0); assert_eq!(shift_reg.get_serial_mode(), SerialMode::Hold); } #[test] fn test_intel4003_debug_function() { let mut shift_reg = Intel4003::new("DebugTestSHIFT".to_string()); // Test debug state transitions shift_reg.set_shift_register(0x123); shift_reg.set_input_buffer(0x45); shift_reg.set_output_buffer(0x67); // Call debug function shift_reg.debug_state_transitions("TestDebug"); // Should not panic or crash assert_eq!(shift_reg.get_shift_register(), 0x123); assert_eq!(shift_reg.get_input_buffer(), 0x45); assert_eq!(shift_reg.get_output_buffer(), 0x67); } }