use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; use crate::component::{BaseComponent, Component, RunnableComponent}; use crate::pin::{Pin, PinValue}; /// Intel 4003 - 10-bit Output Shift Register /// Part of the MCS-4 family, designed to work with Intel 4004 CPU /// Features 10-bit serial-in, parallel-out shift register for output expansion /// /// Hardware Architecture: /// - 10-bit shift register with serial input and parallel outputs /// - Used for expanding I/O capabilities of the MCS-4 system /// - Serial data input from CPU, parallel outputs to external devices /// - Clocked by two-phase clock from 4004 CPU /// /// Hardware Deviations: /// - Simplified timing model for unit testing (may need refinement for CPU integration) /// - Output behavior matches 4001/4002 I/O persistence model pub struct Intel4003 { base: BaseComponent, shift_register: [u8; 10], // 10-bit shift register (stored as 10 bytes for simplicity) output_latch: [u8; 10], // 10-bit output latch for parallel output serial_input: u8, // Serial input data (4-bit) access_time: Duration, // Shift register access latency (200ns typical) // Clock edge detection prev_phi1: PinValue, // Previous Φ1 clock state for edge detection prev_phi2: PinValue, // Previous Φ2 clock state for edge detection // Two-phase addressing for 8-bit address address_high_nibble: Option, // High nibble of 8-bit address address_low_nibble: Option, // Low nibble of 8-bit address full_address_ready: bool, // Whether complete address is assembled // Shift register operation state machine shift_state: ShiftState, // Current state of shift operation address_latch_time: Option, // Timestamp when address was latched } /// Shift register operation state machine states /// Tracks the current phase of shift register operations #[derive(Debug, Clone, Copy, PartialEq)] enum ShiftState { Idle, // No shift operation in progress AddressPhase, // Currently latching address nibbles WaitLatency, // Address latched, waiting for access time ShiftData, // Shifting data into register OutputData, // Outputting parallel data } 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 } /// 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 (based on MCS-4 architecture): // - 4 data pins (D0-D3) for multiplexed address/data // - 10 output pins (O0-O9) for parallel output // - Control pins: SYNC, CM, RESET // - Clock pins: Φ1, Φ2 (two-phase clock from 4004 CPU) // // Control pin behavior: // - SYNC: Marks start of instruction cycle // - CM: Chip select (must be HIGH for shift register access) // - RESET: Clears internal state let pin_names = vec![ "D0", "D1", "D2", "D3", // Data/Address pins "O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins "SYNC", // Sync signal "CM", // Chip Select "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: [0u8; 10], // 10-bit shift register output_latch: [0u8; 10], // 10-bit output latch serial_input: 0, access_time: Duration::from_nanos(access_time_ns), // Clock edge detection prev_phi1: PinValue::Low, prev_phi2: PinValue::Low, // Two-phase addressing address_high_nibble: None, address_low_nibble: None, full_address_ready: false, // Shift register operation state shift_state: ShiftState::Idle, address_latch_time: None, } } /// Set the memory 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); } /// Get the current access time /// Returns: Access time in nanoseconds pub fn get_access_time(&self) -> u64 { self.access_time.as_nanos() as u64 } /// 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.base.get_pin(&format!("D{}", i)) { if let Ok(pin_guard) = pin.lock() { let pin_value = pin_guard.read(); if pin_value == PinValue::High { data |= 1 << i; } println!("DEBUG: 4003 read_data_bus - D{} = {:?}", i, pin_value); } } } let result = data & 0x0F; println!("DEBUG: 4003 read_data_bus returning 0x{:x}", result); result } /// Drive the 4-bit data bus with the specified value /// Parameters: data - 4-bit value to drive on D0-D3 pins /// Update output pins based on current output latch values /// Hardware: Output pins are driven continuously until changed or reset fn update_output_pins(&self) { for i in 0..10 { if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) { if let Ok(mut pin_guard) = pin.lock() { let bit_value = (self.output_latch[i]) & 1; let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.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.base.get_pin(&format!("D{}", i)) { if let Ok(mut pin_guard) = pin.lock() { pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ); } } } } /// Tri-state all output pins fn tri_state_output_pins(&self) { for i in 0..10 { if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) { if let Ok(mut pin_guard) = pin.lock() { pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.name())), PinValue::HighZ); } } } } /// Read the two-phase clock pins from CPU /// Returns: (PHI1_value, PHI2_value) fn read_clock_pins(&self) -> (PinValue, PinValue) { let phi1 = if let Ok(pin) = self.base.get_pin("PHI1") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() } else { PinValue::Low } } else { PinValue::Low }; let phi2 = if let Ok(pin) = self.base.get_pin("PHI2") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() } else { PinValue::Low } } else { PinValue::Low }; (phi1, phi2) } /// Check for Φ1 rising edge /// Check for Φ2 rising edge /// Read all control pins from CPU /// Returns: (sync, chip_select, reset) fn read_control_pins(&self) -> (bool, bool, bool) { let sync = if let Ok(pin) = self.base.get_pin("SYNC") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { false } } else { false }; let chip_select = if let Ok(pin) = self.base.get_pin("CM") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { false } } else { false }; let reset = if let Ok(pin) = self.base.get_pin("RESET") { if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { false } } else { false }; (sync, chip_select, reset) } /// Handle system reset signal fn handle_reset(&mut self) { let (_, _, reset) = self.read_control_pins(); if reset { // RESET is high - clear all internal state self.shift_register = [0u8; 10]; self.output_latch = [0u8; 10]; self.serial_input = 0; // Reset state machine self.shift_state = ShiftState::Idle; self.address_latch_time = None; self.address_high_nibble = None; self.address_low_nibble = None; self.full_address_ready = false; // Tri-state all outputs self.tri_state_data_bus(); self.tri_state_output_pins(); } } /// Assemble complete 8-bit address from high and low nibbles fn assemble_full_address(&mut self) { if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) { // Assemble 8-bit address: (high << 4) | low self.full_address_ready = true; self.address_latch_time = Some(Instant::now()); // Clear nibble storage for next address self.address_high_nibble = None; self.address_low_nibble = None; } } /// Handle Φ1 rising edge - Address and control phase fn handle_phi1_rising(&mut self) { // Handle system reset first self.handle_reset(); // Check for shift register operation start let (sync, chip_select, _) = self.read_control_pins(); if sync && chip_select { self.start_shift_address_phase(); } // Handle shift register address operations self.handle_shift_address_operations(); } /// Handle Φ2 rising edge - Data phase fn handle_phi2_rising(&mut self) { self.handle_shift_data_operations(); } /// Handle shift register address operations during Φ1 fn handle_shift_address_operations(&mut self) { match self.shift_state { ShiftState::Idle => { self.tri_state_data_bus(); } ShiftState::AddressPhase => { self.handle_address_latching(); } ShiftState::WaitLatency => { self.handle_latency_wait(); } ShiftState::ShiftData | ShiftState::OutputData => { self.tri_state_data_bus(); } } } /// Handle shift register data operations during Φ2 fn handle_shift_data_operations(&mut self) { match self.shift_state { ShiftState::Idle => { self.tri_state_data_bus(); } ShiftState::AddressPhase => { self.tri_state_data_bus(); } ShiftState::WaitLatency => { self.handle_latency_wait(); if self.shift_state == ShiftState::ShiftData { self.handle_shift_operation(); } } ShiftState::ShiftData => { self.handle_shift_operation(); } ShiftState::OutputData => { self.handle_output_operation(); } } } /// Start shift register address phase fn start_shift_address_phase(&mut self) { self.shift_state = ShiftState::AddressPhase; self.full_address_ready = false; } /// Handle address nibble latching fn handle_address_latching(&mut self) { let nibble = self.read_data_bus(); if self.address_high_nibble.is_none() { self.address_high_nibble = Some(nibble); } else if self.address_low_nibble.is_none() { self.address_low_nibble = Some(nibble); self.assemble_full_address(); self.start_latency_wait(); } } /// Transition to latency wait state fn start_latency_wait(&mut self) { self.shift_state = ShiftState::WaitLatency; self.address_latch_time = Some(Instant::now()); } /// Handle latency timing fn handle_latency_wait(&mut self) { if let Some(latch_time) = self.address_latch_time { if latch_time.elapsed() >= self.access_time { self.start_shift_operation(); } } } /// Transition to shift operation state fn start_shift_operation(&mut self) { self.shift_state = ShiftState::ShiftData; } /// Handle shift operation fn handle_shift_operation(&mut self) { let (sync, chip_select, _) = self.read_control_pins(); if sync && chip_select && self.full_address_ready { // Read serial input data from data bus let serial_data = self.read_data_bus(); println!("DEBUG: 4003 shift operation - serial_data = 0x{:x}", serial_data); // Shift in the new bits (serial input) // For 4003, we shift in 4 bits at a time // First, shift existing bits right by 4 positions for j in (4..10).rev() { self.shift_register[j] = self.shift_register[j-4]; } // Then insert the 4 new bits into positions 0-3 for i in 0..4 { let bit = (serial_data >> i) & 1; self.shift_register[i] = bit; println!("DEBUG: 4003 shift step {} - inserted bit {} at position {}", i, bit, i); } // Update output latch with new shift register contents self.output_latch.copy_from_slice(&self.shift_register); self.update_output_pins(); println!("DEBUG: 4003 shift register after operation: {:?}", self.shift_register); let (high, low) = self.get_shift_register(); println!("DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}", high, low); self.shift_state = ShiftState::OutputData; } else { self.tri_state_data_bus(); } } /// Handle output operation fn handle_output_operation(&mut self) { // Output operation - parallel data is already available on output pins // This state maintains the output until next operation } /// Get the current shift register value /// Returns: 10-bit value as a tuple (high_byte, low_2_bits) pub fn get_shift_register(&self) -> (u8, u8) { let mut high_byte = 0u8; let mut low_2_bits = 0u8; for i in 0..8 { if self.shift_register[i] != 0 { high_byte |= 1 << i; } } for i in 0..2 { if self.shift_register[8 + i] != 0 { low_2_bits |= 1 << i; } } (high_byte, low_2_bits) } /// Get the current output latch value /// Returns: 10-bit value as a tuple (high_byte, low_2_bits) pub fn get_output_latch(&self) -> (u8, u8) { let mut high_byte = 0u8; let mut low_2_bits = 0u8; for i in 0..8 { if self.output_latch[i] != 0 { high_byte |= 1 << i; } } for i in 0..2 { if self.output_latch[8 + i] != 0 { low_2_bits |= 1 << i; } } (high_byte, low_2_bits) } /// Set serial input data /// Parameters: data - 4-bit serial input data pub fn set_serial_input(&mut self, data: u8) { self.serial_input = data & 0x0F; } /// Clear the shift register pub fn clear_shift_register(&mut self) { self.shift_register = [0u8; 10]; self.output_latch = [0u8; 10]; self.update_output_pins(); } } 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 - handles clock edge detection and operation dispatch fn update(&mut self) { // Handle both rising and falling edges for proper two-phase operation let (phi1, phi2) = self.read_clock_pins(); let phi1_rising = phi1 == PinValue::High && self.prev_phi1 == PinValue::Low; let phi2_rising = phi2 == PinValue::High && self.prev_phi2 == PinValue::Low; // Update clock states for next edge detection self.prev_phi1 = phi1; self.prev_phi2 = phi2; if phi1_rising { // Φ1 Rising Edge: Address phase self.handle_phi1_rising(); } if phi2_rising { // Φ2 Rising Edge: Data phase self.handle_phi2_rising(); } } /// Run component in time-slice mode fn run(&mut self) { self.base.set_running(true); // Initialize clock states let (phi1, phi2) = self.read_clock_pins(); self.prev_phi1 = phi1; self.prev_phi2 = phi2; while self.is_running() { self.update(); thread::sleep(Duration::from_micros(1)); } } /// Stop component and tri-state all outputs fn stop(&mut self) { self.base.set_running(false); self.tri_state_data_bus(); self.tri_state_output_pins(); self.address_latch_time = None; } fn is_running(&self) -> bool { self.base.is_running() } } impl RunnableComponent for Intel4003 {} #[cfg(test)] mod tests { use super::*; #[test] fn test_intel4003_creation() { let sr = Intel4003::new("SHIFT_4003".to_string()); assert_eq!(sr.name(), "SHIFT_4003"); assert_eq!(sr.get_access_time(), 200); // Default 200ns assert!(!sr.is_running()); } #[test] fn test_intel4003_shift_operation() { let mut sr = Intel4003::new_with_access_time("SHIFT_4003".to_string(), 1); // Set up the shift register with some initial data sr.shift_register = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]; // 1010101010 pattern // Set up the component for shift operation by setting the required state sr.shift_state = ShiftState::ShiftData; sr.full_address_ready = true; // Set data on data bus (0x05 = 0101 binary) for shift operation let d0_pin = sr.get_pin("D0").unwrap(); let d1_pin = sr.get_pin("D1").unwrap(); let d2_pin = sr.get_pin("D2").unwrap(); let d3_pin = sr.get_pin("D3").unwrap(); { let mut d0_guard = d0_pin.lock().unwrap(); let mut d1_guard = d1_pin.lock().unwrap(); let mut d2_guard = d2_pin.lock().unwrap(); let mut d3_guard = d3_pin.lock().unwrap(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 0 = 1 d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0 d2_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 2 = 1 d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0 } // Set control pins for shift operation let sync_pin = sr.get_pin("SYNC").unwrap(); let cm_pin = sr.get_pin("CM").unwrap(); { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Perform shift operation sr.handle_shift_operation(); // Verify shift operation occurred let (high, low) = sr.get_shift_register(); println!("DEBUG: After shift operation - high=0x{:x}, low=0x{:x}", high, low); println!("DEBUG: Shift register contents: {:?}", sr.shift_register); // The shift register should now contain the shifted data // Since we shifted in 0x05 (binary 0101), the register should contain this pattern // The first 4 bits should be 0101 (0x05), and the rest should be the original pattern shifted // The current result shows high=0x55, low=0x1 which means: // high byte: 01010101 (0x55) - this is correct, the first 4 bits are 0101 (0x05) // low byte: 00000001 (0x01) - this is the remaining 2 bits from the original pattern assert_eq!(high, 0x55); // First 8 bits should be 01010101 assert_eq!(low, 0x01); // Lower 2 bits should be 01 } #[test] fn test_intel4003_reset() { let mut sr = Intel4003::new("SHIFT_4003".to_string()); // Set some data first sr.set_serial_input(0x0F); sr.shift_register[0] = 1; // Verify data is set assert_eq!(sr.serial_input, 0x0F); assert_eq!(sr.shift_register[0], 1); // Apply reset let reset_pin = sr.get_pin("RESET").unwrap(); { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Call handle_reset directly to ensure reset is processed sr.handle_reset(); // Verify reset cleared everything assert_eq!(sr.serial_input, 0); assert_eq!(sr.shift_register[0], 0); assert_eq!(sr.shift_state, ShiftState::Idle); } #[test] fn test_configurable_access_time() { let mut sr = Intel4003::new("SHIFT_4003".to_string()); // Test default access time assert_eq!(sr.get_access_time(), 200); // Test setting custom access time sr.set_access_time(100); assert_eq!(sr.get_access_time(), 100); // Test constructor with custom access time let fast_sr = Intel4003::new_with_access_time("FAST_SHIFT".to_string(), 1); assert_eq!(fast_sr.get_access_time(), 1); assert_eq!(fast_sr.name(), "FAST_SHIFT"); } }