diff --git a/fibonacci.bin b/fibonacci.bin new file mode 100644 index 0000000..a875cfe --- /dev/null +++ b/fibonacci.bin @@ -0,0 +1 @@ +-n -e '\x20\x00\x10\x00\x50\x21\x01\x10\x01\x50\x00\x02\x76\x00\x02\x00\x00\x10\x01\x50\x00\x01\x00\x00\x50\x00\x02\x30\x08\x40\x0C\x00\x00' diff --git a/src/components/memory/intel_4003.rs b/src/components/memory/intel_4003.rs new file mode 100644 index 0000000..eda59db --- /dev/null +++ b/src/components/memory/intel_4003.rs @@ -0,0 +1,673 @@ +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"); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6e78f65..9999688 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,132 +1,242 @@ use rusty_emu::systems::intel_mcs_4::IntelMcs4; +use std::env; +use std::fs; +use std::process; use std::thread; use std::time::{Duration, Instant}; fn main() { - println!("Intel MCS-4 Emulator - Fibonacci Sequence Example"); - println!("================================================="); + // Parse command line arguments + let args: Vec = env::args().collect(); + let mut system_type = "mcs4".to_string(); + let mut program_file = "fibonacci.bin".to_string(); - // Create the MCS-4 system - let mut mcs4 = IntelMcs4::new(); - - // Load the Fibonacci program - let fibonacci_program = compile_fibonacci_program(); - - // Split program between ROMs - let rom1_size = 256; - let rom1_data = if fibonacci_program.len() > rom1_size { - fibonacci_program[..rom1_size].to_vec() - } else { - fibonacci_program.clone() - }; - - let rom2_data = if fibonacci_program.len() > rom1_size { - fibonacci_program[rom1_size..].to_vec() - } else { - vec![0; 256] - }; - - println!("Loading Fibonacci program into ROM..."); - if let Err(e) = mcs4.load_program(rom1_data, rom2_data) { - eprintln!("Failed to load program: {}", e); - return; + let mut i = 1; + while i < args.len() { + match args[i].as_str() { + "-s" | "--system" => { + if i + 1 < args.len() { + system_type = args[i + 1].clone(); + i += 2; + } else { + eprintln!("Error: --system requires a value"); + print_usage(&args[0]); + process::exit(1); + } + } + "-f" | "--file" => { + if i + 1 < args.len() { + program_file = args[i + 1].clone(); + i += 2; + } else { + eprintln!("Error: --file requires a value"); + print_usage(&args[0]); + process::exit(1); + } + } + "-h" | "--help" => { + print_usage(&args[0]); + return; + } + _ => { + eprintln!("Unknown argument: {}", args[i]); + print_usage(&args[0]); + process::exit(1); + } + } } - // Initialize RAM with Fibonacci seeds - let initial_data = [0x00, 0x01]; - if let Err(e) = mcs4.load_ram_data(&initial_data, 0) { - eprintln!("Failed to initialize RAM: {}", e); - return; - } + println!("Intel MCS-4 Emulator"); + println!("==================="); + println!("System: {}", system_type); + println!("Program: {}", program_file); - mcs4.reset_system(); + // Load program data + let program_data = match load_program_data(&program_file) { + Ok(data) => data, + Err(e) => { + eprintln!("Failed to load program: {}", e); + process::exit(1); + } + }; + + // Create and configure the system + let system = match create_system(&system_type, &program_data) { + Ok(sys) => sys, + Err(e) => { + eprintln!("Failed to create system: {}", e); + process::exit(1); + } + }; // Display system information - let info = mcs4.get_system_info(); + let info = system.get_system_info(); println!("\nSystem Information:"); println!(" CPU Speed: {} Hz", info.cpu_speed); println!(" ROM Size: {} bytes", info.rom_size); println!(" RAM Size: {} nibbles", info.ram_size); println!(" Components: {}", info.component_count); - println!("\nStarting Fibonacci sequence calculation..."); + println!("\nStarting execution..."); println!("Press Ctrl+C to stop execution"); println!(); - // Run the Fibonacci demo - run_fibonacci_demo(mcs4); + // Run the system + run_system_demo(system); } -fn compile_fibonacci_program() -> Vec { - // Simple program that just runs and lets the CPU simulate Fibonacci +fn print_usage(program_name: &str) { + println!("Usage: {} [OPTIONS]", program_name); + println!(); + println!("Options:"); + println!(" -s, --system System type to run (default: mcs4)"); + println!(" -f, --file Program binary file to load (default: fibonacci.bin)"); + println!(" -h, --help Show this help message"); + println!(); + println!("Examples:"); + println!(" {} -s mcs4 -f fibonacci.bin", program_name); + println!(" {} --system mcs4 --file myprogram.bin", program_name); +} + +fn load_program_data(filename: &str) -> Result, String> { + println!("DEBUG: Attempting to load program from: {}", filename); + match fs::read(filename) { + Ok(data) => { + println!("DEBUG: Successfully loaded {} bytes from {}", data.len(), filename); + Ok(data) + } + Err(e) => { + // If file doesn't exist, try to use default program + if filename == "fibonacci.bin" { + println!("DEBUG: File {} not found ({}), using default fibonacci program", filename, e); + let default_program = get_default_fibonacci_program(); + println!("DEBUG: Default program size: {} bytes", default_program.len()); + Ok(default_program) + } else { + println!("DEBUG: Failed to read file {}: {}", filename, e); + Err(format!("Failed to read file {}: {}", filename, e)) + } + } + } +} + +fn get_default_fibonacci_program() -> Vec { + // Fibonacci program for Intel 4004 + // This implements a simple Fibonacci sequence generator vec![ - 0xF0, // LDM 0 - 0xF1, // LDM 1 - 0x30, // SRC 0 - 0x31, // SRC 1 - 0x60, // JUN 0 - 0x00, // Address - // Pad with zeros + 0x20, 0x00, // LDM 0 (Load accumulator with 0) - First Fibonacci number + 0x10, 0x00, // LD 0 (Load accumulator from register 0) + 0x50, // WRM (Write accumulator to RAM at current pointer) + 0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number + 0x10, 0x01, // LD 1 (Load accumulator from register 1) + 0x50, // WRM (Write accumulator to RAM) + + // Fibonacci calculation loop + 0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter + 0x76, // IAC (Increment accumulator) + 0x00, 0x02, // LD 2 (Store back to register 2) + + // Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2) + 0x00, 0x00, // LD 0 (Load F(n-2) into accumulator) + 0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator) + 0x50, // WRM (Store result to RAM) + + // Update registers for next iteration + 0x00, 0x01, // LD 1 (Load F(n-1) into accumulator) + 0x00, 0x00, // LD 0 (Store F(n-1) to register 0) + 0x50, // WRM (Write to RAM - this will be read back as F(n-2)) + + // Check if we've calculated enough numbers (8 iterations) + 0x00, 0x02, // LD 2 (Load loop counter) + 0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition + 0x40, 0x0C, // JCN 12 (Jump back to loop start) + + // Exit - halt program + 0x00, 0x00, // NOP (placeholder for halt) ] } -fn run_fibonacci_demo(mcs4: IntelMcs4) { +fn create_system(system_type: &str, program_data: &[u8]) -> Result { + match system_type { + "mcs4" => { + let system = IntelMcs4::new(); + + // Split program between ROMs + let rom1_size = 256; + let rom1_data = if program_data.len() > rom1_size { + program_data[..rom1_size].to_vec() + } else { + program_data.to_vec() + }; + + let rom2_data = if program_data.len() > rom1_size { + program_data[rom1_size..].to_vec() + } else { + vec![0; 256] + }; + + // Load program into ROMs + let mut system = system; + system.load_program(rom1_data, rom2_data) + .map_err(|e| format!("Failed to load program: {}", e))?; + + // Initialize RAM with some data for the program + let initial_data = [0x00, 0x01]; + system.load_ram_data(&initial_data, 0) + .map_err(|e| format!("Failed to initialize RAM: {}", e))?; + + system.reset_system(); + Ok(system) + } + _ => Err(format!("Unknown system type: {}", system_type)) + } +} + +fn run_system_demo(system: IntelMcs4) { let start_time = Instant::now(); - println!("Cycle | Fibonacci | Accumulator | PC"); - println!("------|-----------|-------------|------"); + println!("Cycle | Accumulator | PC"); + println!("------|-------------|------"); // Run system in a separate thread - let mcs4_arc = std::sync::Arc::new(std::sync::Mutex::new(mcs4)); - let mcs4_clone = mcs4_arc.clone(); + let system_arc = std::sync::Arc::new(std::sync::Mutex::new(system)); + let system_clone = system_arc.clone(); let handle = thread::spawn(move || { - if let Ok(mut system) = mcs4_clone.lock() { + if let Ok(mut system) = system_clone.lock() { system.run(); } }); // Monitor CPU state in main thread - let mut last_fibonacci = 0; let mut iteration = 0; while iteration < 20 { - // Run for 20 iterations thread::sleep(Duration::from_millis(200)); - if let Ok(system) = mcs4_arc.lock() { + if let Ok(system) = system_arc.lock() { if let Ok(state) = system.get_cpu_state() { - let current_fibonacci = state.accumulator; - - if current_fibonacci != last_fibonacci { - println!( - "{:5} | {:9} | {:11} | {:04X}", - state.cycle_count, - current_fibonacci, - state.accumulator, - state.program_counter - ); - last_fibonacci = current_fibonacci; - iteration += 1; - } + println!( + "{:5} | {:11} | {:04X}", + state.cycle_count, + state.accumulator, + state.program_counter + ); + iteration += 1; } } if iteration >= 15 { - // Stop after 15 Fibonacci numbers - if let Ok(mut system) = mcs4_arc.lock() { + if let Ok(mut system) = system_arc.lock() { system.stop(); } break; } } - // Wait for system thread to finish let _ = handle.join(); - let duration = start_time.elapsed(); println!("\nExecution completed in {:?}", duration); - println!("Fibonacci demo finished."); } #[cfg(test)] @@ -135,7 +245,8 @@ mod tests { #[test] fn test_fibonacci_program() { - let program = compile_fibonacci_program(); + let program = get_default_fibonacci_program(); assert!(!program.is_empty()); + println!("Fibonacci program size: {} bytes", program.len()); } -} +} \ No newline at end of file diff --git a/src/systems/intel_mcs_4.rs b/src/systems/intel_mcs_4.rs index de91813..5429952 100644 --- a/src/systems/intel_mcs_4.rs +++ b/src/systems/intel_mcs_4.rs @@ -14,61 +14,18 @@ use crate::pin::Pin; pub struct IntelMcs4 { components: HashMap>>, is_running: bool, - fibonacci_program: Vec, // 4004 assembly program for Fibonacci calculation } impl IntelMcs4 { pub fn new() -> Self { - let mut system = IntelMcs4 { + let system = IntelMcs4 { components: HashMap::new(), is_running: false, - fibonacci_program: Vec::new(), }; - system.initialize_fibonacci_program(); - system.initialize_system(); system } - /// Initialize the Fibonacci calculation program for 4004 assembly - /// This program calculates Fibonacci numbers and stores them in RAM - fn initialize_fibonacci_program(&mut self) { - // 4004 Assembly program for Fibonacci calculation - // This is a simplified version that calculates the first few Fibonacci numbers - self.fibonacci_program = vec![ - // Program start - Initialize registers - 0x20, 0x00, // LDM 0 (Load accumulator with 0) - First Fibonacci number - 0x10, 0x00, // LD 0 (Load accumulator from register 0) - 0x50, // WRM (Write accumulator to RAM at current pointer) - 0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number - 0x10, 0x01, // LD 1 (Load accumulator from register 1) - 0x50, // WRM (Write accumulator to RAM) - - // Fibonacci calculation loop - 0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter - 0x76, // IAC (Increment accumulator) - 0x00, 0x02, // LD 2 (Store back to register 2) - - // Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2) - 0x00, 0x00, // LD 0 (Load F(n-2) into accumulator) - 0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator) - 0x50, // WRM (Store result to RAM) - - // Update registers for next iteration - 0x00, 0x01, // LD 1 (Load F(n-1) into accumulator) - 0x00, 0x00, // LD 0 (Store F(n-1) to register 0) - 0x50, // WRM (Write to RAM - this will be read back as F(n-2)) - - // Check if we've calculated enough numbers (8 iterations) - 0x00, 0x02, // LD 2 (Load loop counter) - 0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition - 0x40, 0x0C, // JCN 12 (Jump back to loop start) - - // Exit - halt program - 0x00, 0x00, // NOP (placeholder for halt) - ]; - } - fn initialize_system(&mut self) { // Create CPU with proper clock speed let cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0); @@ -107,9 +64,6 @@ impl IntelMcs4 { // Connect data bus between CPU and memory components self.connect_data_bus(); - - // Load Fibonacci program into ROM - self.load_fibonacci_program(); } /// Connect clock signals from clock generator to all components @@ -302,20 +256,11 @@ impl IntelMcs4 { } } - /// Load the Fibonacci program into ROM - fn load_fibonacci_program(&mut self) { - // For now, just log that the program is loaded - // In a real implementation, we would need to redesign the interface - // to allow loading data into components after creation - println!("Loaded {} bytes of Fibonacci program into ROM1", self.fibonacci_program.len()); - } - pub fn run(&mut self) { self.is_running = true; let mut handles = vec![]; println!("Starting MCS-4 system components..."); - println!("Fibonacci program loaded into ROM ({} bytes)", self.fibonacci_program.len()); for (name, component) in &self.components { let comp_clone = Arc::clone(component); @@ -333,9 +278,9 @@ impl IntelMcs4 { } println!("All components started. System running..."); - println!("CPU will execute Fibonacci calculation program..."); + println!("CPU will execute loaded program..."); - // Monitor system and display Fibonacci results + // Monitor system and display system status let mut last_cycle_count = 0; let mut display_counter = 0; @@ -344,9 +289,9 @@ impl IntelMcs4 { // Get current CPU state if let Ok(cpu_state) = self.get_cpu_state() { - // Display Fibonacci results periodically + // Display system status periodically if cpu_state.cycle_count - last_cycle_count > 100 { - self.display_fibonacci_results(); + self.display_system_status(); last_cycle_count = cpu_state.cycle_count; display_counter += 1; } @@ -376,15 +321,13 @@ impl IntelMcs4 { } println!("MCS-4 system stopped."); - println!("\nFinal Fibonacci results in RAM:"); - self.display_fibonacci_results(); + println!("\nFinal system status:"); + self.display_system_status(); } - /// Display the current Fibonacci calculation results from RAM - fn display_fibonacci_results(&self) { - // For now, just display CPU state since we can't easily access RAM data - // In a real implementation, we would need to redesign the interface - println!("Fibonacci sequence calculation in progress..."); + /// Display the current system status + fn display_system_status(&self) { + println!("System execution in progress..."); if let Ok(cpu_state) = self.get_cpu_state() { println!("CPU State - PC: 0x{:03X}, ACC: 0x{:X}, Cycles: {}", @@ -494,11 +437,6 @@ impl IntelMcs4 { ] } - /// Get the Fibonacci program that will be executed - pub fn get_fibonacci_program(&self) -> &[u8] { - &self.fibonacci_program - } - /// Get detailed system status including all component states pub fn get_detailed_status(&self) -> SystemStatus { let cpu_state = self.get_cpu_state().ok(); @@ -508,7 +446,6 @@ impl IntelMcs4 { is_running: self.is_running, cpu_state, system_info, - fibonacci_program_size: self.fibonacci_program.len(), components_status: self.get_components_status(), } } @@ -567,7 +504,6 @@ pub struct SystemStatus { pub is_running: bool, pub cpu_state: Option, pub system_info: SystemInfo, - pub fibonacci_program_size: usize, pub components_status: HashMap, } @@ -600,4 +536,4 @@ impl Component for IntelMcs4 { fn is_running(&self) -> bool { self.is_running() } -} +} \ No newline at end of file