Extracted fibonacci code into binary file

System and binary selection upon start
This commit is contained in:
Jan-Peter von Hunnius 2025-09-28 22:23:23 +02:00
parent e7c98f0c47
commit dd93e33087
4 changed files with 871 additions and 150 deletions

1
fibonacci.bin Normal file
View file

@ -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'

View file

@ -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<u8>, // High nibble of 8-bit address
address_low_nibble: Option<u8>, // 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<Instant>, // 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<String, Arc<Mutex<Pin>>> {
self.base.pins()
}
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, 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");
}
}

View file

@ -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<String> = 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<u8> {
// 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> System type to run (default: mcs4)");
println!(" -f, --file <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<Vec<u8>, 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<u8> {
// 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<IntelMcs4, String> {
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());
}
}
}

View file

@ -14,61 +14,18 @@ use crate::pin::Pin;
pub struct IntelMcs4 {
components: HashMap<String, Arc<Mutex<dyn Component>>>,
is_running: bool,
fibonacci_program: Vec<u8>, // 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<CpuState>,
pub system_info: SystemInfo,
pub fibonacci_program_size: usize,
pub components_status: HashMap<String, String>,
}
@ -600,4 +536,4 @@ impl Component for IntelMcs4 {
fn is_running(&self) -> bool {
self.is_running()
}
}
}