diff --git a/src/bus.rs b/src/bus.rs new file mode 100644 index 0000000..45dc429 --- /dev/null +++ b/src/bus.rs @@ -0,0 +1,162 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use crate::{Component, Pin, PinValue}; +pub trait BusDevice: Send { + /// Called on each simulation cycle to update device state based on pin states + fn update(&mut self); + + /// Connect this device to bus pins + fn connect_to_bus(&mut self, bus_pins: Vec>); + + /// Optional: report address range for debugging/monitoring + fn get_address_range(&self) -> Option> { + None + } + + /// Optional: get device name for debugging + fn get_name(&self) -> &str; +} +#[derive(Clone)] +pub struct GenericBus { + base: crate::component::BaseComponent, + connected_devices: Vec>>, + bus_pins: HashMap>, // Named bus lines (like "A0", "D0", "R/W") +} + +impl GenericBus { + pub fn new(name: String, pin_definitions: &[(&str, PinValue)]) -> Self { + let mut base = crate::component::BaseComponent::new(name.clone()); + let mut bus_pins = HashMap::new(); + + // Create bus pins + for (pin_name, initial_value) in pin_definitions { + let pin = base.add_pin( + pin_name.to_string(), + *initial_value, + false, // Bus doesn't drive pins by default + ); + bus_pins.insert(pin_name.to_string(), pin); + } + + Self { + base, + connected_devices: Vec::new(), + bus_pins, + } + } + + pub fn connect_device(&mut self, device: Arc>) { + // Get all bus pins and connect them to the device + let bus_pins: Vec> = self.bus_pins.values().cloned().collect(); + + if let Ok(mut device) = device.lock() { + device.connect_to_bus(bus_pins); + } + + self.connected_devices.push(device); + } + + pub fn get_pin(&self, name: &str) -> Option> { + self.bus_pins.get(name).cloned() + } + + /// The key function: resolve pin conflicts and propagate values + fn resolve_bus_conflicts(&self) { + for (pin_name, bus_pin) in &self.bus_pins { + self.resolve_pin_state(bus_pin); + } + } + + fn resolve_pin_state(&self, bus_pin: &Arc) { + let connections = bus_pin.connections.lock().unwrap(); + + // Check if any device is driving the pin low (wired-AND behavior) + let mut any_driving_low = false; + let mut any_driving_high = false; + let mut any_driving = false; + + for connected_pin in connections.iter() { + let output_enable = connected_pin.output_enable.lock().unwrap(); + let value = connected_pin.value.read().unwrap(); + + if *output_enable { + any_driving = true; + match *value { + PinValue::Low => any_driving_low = true, + PinValue::High => any_driving_high = true, + PinValue::HighZ => {} // High-Z doesn't drive + } + } + } + + // Determine the resolved bus state + let resolved_value = if any_driving_low { + // If any device pulls low, bus is low (wired-AND) + PinValue::Low + } else if any_driving_high && !any_driving_low { + // If devices are driving high but no one is pulling low + PinValue::High + } else if any_driving { + // Mixed driving states (shouldn't happen in proper design) + PinValue::Low // Safe fallback: low wins + } else { + // No one is driving - high impedance + PinValue::HighZ + }; + + // Update the bus pin to reflect the resolved state + // BUT: the bus itself doesn't drive the pin - it just reflects the resolved state + // In real hardware, the bus pin's state emerges from the connected devices + drop(connections); + + // We need to update the pin's value without "driving" it + // This is tricky because our Pin struct couples value with output enable + // Let's create a helper method to update the resolved value + self.update_pin_resolved_value(bus_pin, resolved_value); + } + + fn update_pin_resolved_value(&self, pin: &Arc, value: PinValue) { + // This is a challenge: we want to set the pin's value without claiming to "drive" it + // We need to extend our Pin struct to support bus resolution + let mut current_value = pin.value.write().unwrap(); + *current_value = value; + // Note: we don't touch output_enable - the bus isn't driving, just reflecting + } +} + +impl Component for GenericBus { + fn name(&self) -> &str { + self.base.name() + } + + fn pins(&self) -> &std::collections::HashMap> { + self.base.pins() + } + + fn get_pin(&self, name: &str) -> Option> { + self.base.get_pin(name) + } + + fn update(&mut self) -> Result<(), String> { + // Bus update cycle: + // 1. First, let all devices update based on current pin states + for device in &self.connected_devices { + if let Ok(mut device) = device.lock() { + device.update(); + } + } + + // 2. Then resolve bus conflicts and propagate pin states + self.resolve_bus_conflicts(); + + Ok(()) + } + + fn run(&mut self) { + self.base.run(); + } + + fn stop(&mut self) { + self.base.stop(); + } +} \ No newline at end of file diff --git a/src/components/clock/generic_clock.rs b/src/components/clock/generic_clock.rs index d3ad1bc..cb5f5cd 100644 --- a/src/components/clock/generic_clock.rs +++ b/src/components/clock/generic_clock.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use std::time::Duration; use crate::component::{BaseComponent, Component}; @@ -8,7 +8,6 @@ use crate::pin::{Pin, PinValue}; #[derive(Clone)] pub struct GenericClock { base: BaseComponent, - frequency: u64, half_period: Duration, last_state: bool, } @@ -21,7 +20,6 @@ impl GenericClock { Self { base, - frequency: frequency_hz, half_period: Duration::from_millis(half_period_ms), last_state: false, } diff --git a/src/components/cpu/intel_4004.rs b/src/components/cpu/intel_4004.rs index 92d801b..23d4465 100644 --- a/src/components/cpu/intel_4004.rs +++ b/src/components/cpu/intel_4004.rs @@ -1,57 +1,231 @@ use std::collections::HashMap; -use std::sync::Arc; -use crate::component::{BaseComponent, Component}; +use std::sync::{Arc, Mutex}; +use crate::bus::GenericBus; +use crate::component::Component; use crate::pin::{Pin, PinValue}; // Simple 4-bit and 12-bit types for the 4004 #[derive(Debug, Clone, Copy)] -struct u4(u8); +struct U4(u8); #[derive(Debug, Clone, Copy)] -struct u12(u16); -impl u4 { - fn wrapping_add(self, other: u4) -> u4 { - u4((self.0 + other.0) & 0xF) +struct U12(u16); +impl U4 { + fn wrapping_add(self, other: U4) -> U4 { + U4((self.0 + other.0) & 0xF) } } -impl u12 { - fn wrapping_add(self, other: u12) -> u12 { - u12((self.0 + other.0) & 0xFFF) +impl U12 { + fn wrapping_add(self, other: U12) -> U12 { + U12((self.0 + other.0) & 0xFFF) } } #[derive(Clone)] pub struct Intel4004 { - base: BaseComponent, - program_counter: u12, - accumulator: u4, + base: crate::component::BaseComponent, + program_counter: U12, + accumulator: U4, + carry: bool, last_clock_state: PinValue, + registers: [U4; 16], // 4004 has 16 4-bit registers + stack: [U12; 3], // 3-level subroutine stack + stack_pointer: u8, + bus: Option>>, // Connected bus } impl Intel4004 { pub fn new(name: String) -> Self { - let mut base = BaseComponent::new(name); + let mut base = crate::component::BaseComponent::new(name); - // Add essential pins - base.add_pin("clk".to_string(), PinValue::Low, false); // Input pin - base.add_pin("reset".to_string(), PinValue::Low, false); // Input pin - base.add_pin("data".to_string(), PinValue::HighZ, false); // Bidirectional + base.add_pin("clk".to_string(), PinValue::Low, false); + base.add_pin("reset".to_string(), PinValue::Low, false); + base.add_pin("sync".to_string(), PinValue::Low, true); + base.add_pin("data".to_string(), PinValue::HighZ, false); Self { base, - program_counter: u12(0), - accumulator: u4(0), + program_counter: U12(0), + accumulator: U4(0), + carry: false, last_clock_state: PinValue::Low, + registers: [U4(0); 16], + stack: [U12(0); 3], + stack_pointer: 0, + bus: None, } } + + pub fn connect_to_bus(&mut self, bus: Arc>) { + self.bus = Some(bus); + } + + fn read_memory(&self, address: u16) -> u8 { + if let Some(bus) = &self.bus { + let bus = bus.lock().unwrap(); + bus.read(address as u64) as u8 + } else { + 0xFF + } + } + + fn write_memory(&mut self, address: u16, value: u8) { + if let Some(bus) = &self.bus { + let mut bus = bus.lock().unwrap(); + bus.write(address as u64, value as u64); + } + } + + fn fetch_instruction(&mut self) -> u8 { + let instruction = self.read_memory(self.program_counter.0); + self.program_counter = self.program_counter.wrapping_add(U12(1)); + instruction + } + + fn execute(&mut self, instruction: u8) { + match instruction { + // NOP + 0x00 => self.nop(), + + // JCN - Jump Conditional + 0x10 => self.jcn(), + + // FIM - Fetch Immediate + 0x20 => self.fim(), + + // SRC - Send Register Control + //0x21 => self.src(), + + // FIN - Fetch Indirect + //0x30 => self.fin(), + + // JIN - Jump Indirect + //0x31 => self.jin(), + + // LDM - Load Immediate + 0xD0 => self.ldm(), + + // INC - Increment Register + 0x60 => self.inc(), + + // ADD - Add + 0xF0 => self.add(), + + // SUB - Subtract + //0xF1 => self.sub(), + + // LD - Load from Register + //0xA0 => self.ld(), + + // XCH - Exchange + //0xB0 => self.xch(), + + // BBL - Branch Back and Load + //0xC0 => self.bbl(), + + // WRM - Write Main Memory + 0xE0 => self.wrm(), + + // RDM - Read Main Memory + 0xE1 => self.rdm(), + + _ => println!("Unknown instruction: {:02X}", instruction), + } + } + + // Instruction implementations + fn nop(&mut self) { + // No operation + } + + fn jcn(&mut self) { + let condition = self.fetch_instruction(); + let address_low = self.fetch_instruction(); + let address_high = self.fetch_instruction(); + let address = ((address_high as u16) << 8) | address_low as u16; + + let mut jump = false; + + // Check conditions + if condition & 0x01 != 0 && self.carry { jump = true; } + if condition & 0x02 != 0 && self.accumulator.0 == 0 { jump = true; } + if condition & 0x04 != 0 && self.test_pin() { jump = true; } + if condition & 0x08 != 0 { jump = !jump; } + + if jump { + self.program_counter = U12(address); + } + } + + fn fim(&mut self) { + let register_pair = (self.fetch_instruction() & 0x0E) as usize; // Even registers only + let data = self.fetch_instruction(); + + self.registers[register_pair] = U4(data >> 4); + self.registers[register_pair + 1] = U4(data & 0x0F); + } + + fn ldm(&mut self) { + let data = self.fetch_instruction(); + self.accumulator = U4(data & 0x0F); + } + + fn inc(&mut self) { + let register = (self.fetch_instruction() & 0x0F) as usize; + let new_value = self.registers[register].0.wrapping_add(1) & 0x0F; + self.registers[register] = U4(new_value); + } + + fn add(&mut self) { + let register = (self.fetch_instruction() & 0x0F) as usize; + let result = self.accumulator.0 + self.registers[register].0; + self.accumulator = U4(result & 0x0F); + self.carry = result > 0x0F; + } + + fn wrm(&mut self) { + // Write accumulator to main memory + let address = self.get_memory_address(); + self.write_memory(address, self.accumulator.0); + } + + fn rdm(&mut self) { + // Read from main memory to accumulator + let address = self.get_memory_address(); + let data = self.read_memory(address); + self.accumulator = U4(data & 0x0F); + } + + fn get_memory_address(&self) -> u16 { + // Simplified address calculation + // In real 4004, this uses register pairs + 0x200 + self.registers[0].0 as u16 // Example: use register 0 as offset + } + + fn test_pin(&self) -> bool { + // Check test pin (simplified) + false + } + + pub fn get_state(&self) -> String { + format!( + "PC: {:03X} ACC: {:X} C: {} R0: {:X} R1: {:X}", + self.program_counter.0, + self.accumulator.0, + self.carry as u8, + self.registers[0].0, + self.registers[1].0 + ) + } pub fn clk(&self) -> Option> { self.base.get_pin("clk") } + pub fn reset(&self) -> Option> { self.base.get_pin("reset") } + pub fn data(&self) -> Option> { self.base.get_pin("data") } } - impl Component for Intel4004 { fn name(&self) -> &str { self.base.name() @@ -63,28 +237,23 @@ impl Component for Intel4004 { self.base.get_pin(name) } fn update(&mut self) -> Result<(), String> { - // Read clock pin (check if any connected component is driving it low) let clk_pin = self.base.pins.get("clk").unwrap(); let clk_value = clk_pin.read(); // Detect rising edge if clk_value == PinValue::High && self.last_clock_state == PinValue::Low { - // Simple fetch-execute cycle - self.program_counter = self.program_counter.wrapping_add(u12(1)); + // Fetch and execute instruction + let instruction = self.fetch_instruction(); + self.execute(instruction); - // Read reset pin - let reset_pin = self.base.pins.get("reset").unwrap(); - let reset_value = reset_pin.read(); + // Update sync pin + let sync_pin = self.base.pins.get("sync").unwrap(); + sync_pin.write(PinValue::High, true); - if reset_value == PinValue::High { - self.program_counter = u12(0); - self.accumulator = u4(0); - println!("CPU {}: RESET", self.name()); - } else { - // For now, just print state - println!("CPU {}: PC={:03X}, ACC={:X}", - self.name(), self.program_counter.0, self.accumulator.0); - } + println!("{}", self.get_state()); + } else if clk_value == PinValue::Low { + let sync_pin = self.base.pins.get("sync").unwrap(); + sync_pin.write(PinValue::Low, true); } self.last_clock_state = clk_value; diff --git a/src/components/cpu/mos_6502.rs b/src/components/cpu/mos_6502.rs index 5cfd08d..2bfa1c8 100644 --- a/src/components/cpu/mos_6502.rs +++ b/src/components/cpu/mos_6502.rs @@ -115,7 +115,7 @@ impl Component for MOS6502 { fn pins(&self) -> &HashMap> { todo!() } - fn get_pin(&self, name: &str) -> Option> { + fn get_pin(&self, _name: &str) -> Option> { todo!() } // Implement Component trait methods... diff --git a/src/components/memory/generic_ram.rs b/src/components/memory/generic_ram.rs new file mode 100644 index 0000000..62eff66 --- /dev/null +++ b/src/components/memory/generic_ram.rs @@ -0,0 +1,303 @@ +use std::ops::Range; +use std::sync::Arc; +use crate::{Component, Pin, PinValue}; + +#[derive(Clone)] +pub struct GenericRam { + base: crate::component::BaseComponent, + pub(crate) memory: Vec, + base_address: u64, + address_mask: u64, + read_delay: u32, // Read access time in cycles + write_delay: u32, // Write access time in cycles + current_operation: Option, + operation_cycles: u32, + output_buffer: Option, +} + +#[derive(Clone, Copy, PartialEq)] +enum RamOperation { + Read(u64), // address + Write(u64, u64), // address, data +} + +impl +GenericRam +{ + pub fn new(name: String, base_address: u64, read_delay: u32, write_delay: u32) -> Self { + let mut base = crate::component::BaseComponent::new(name); + + // Add address pins + for i in 0..ADDR_WIDTH { + base.add_pin(format!("addr_{}", i), PinValue::Low, false); + } + + // Add data pins + for i in 0..DATA_WIDTH { + base.add_pin(format!("data_{}", i), PinValue::HighZ, false); + } + + // Add control pins + base.add_pin("cs".to_string(), PinValue::High, false); // Chip Select (active low) + base.add_pin("we".to_string(), PinValue::High, false); // Write Enable (active low) + base.add_pin("oe".to_string(), PinValue::High, false); // Output Enable (active low) + base.add_pin("clk".to_string(), PinValue::Low, false); // Clock for synchronization + + // Calculate address mask based on size + let address_mask = ((1 << ADDR_WIDTH) - 1) as u64; + + Self { + base, + memory: vec![0; SIZE], + base_address, + address_mask, + read_delay, + write_delay, + current_operation: None, + operation_cycles: 0, + output_buffer: None, + } + } + + fn read_address_bus(&self) -> u64 { + let mut address = 0; + for i in 0..ADDR_WIDTH { + if let Some(pin) = self.base.get_pin(&format!("addr_{}", i)) { + if pin.read() == PinValue::High { + address |= 1 << i; + } + } + } + address + } + + fn read_data_bus(&self) -> u64 { + let mut data = 0; + for i in 0..DATA_WIDTH { + if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { + if pin.read() == PinValue::High { + data |= 1 << i; + } + } + } + data + } + + fn write_data_bus(&self, data: u64) { + for i in 0..DATA_WIDTH { + if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { + let bit = (data >> i) & 1; + let value = if bit == 1 { PinValue::High } else { PinValue::Low }; + pin.write(value, true); + } + } + } + + fn set_data_bus_high_z(&self) { + for i in 0..DATA_WIDTH { + if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { + pin.write(PinValue::HighZ, false); + } + } + } + + fn is_selected(&self) -> bool { + if let Some(cs_pin) = self.base.get_pin("cs") { + cs_pin.read() == PinValue::Low // Active low + } else { + false + } + } + + fn is_write_enabled(&self) -> bool { + if let Some(we_pin) = self.base.get_pin("we") { + we_pin.read() == PinValue::Low // Active low + } else { + false + } + } + + fn is_output_enabled(&self) -> bool { + if let Some(oe_pin) = self.base.get_pin("oe") { + oe_pin.read() == PinValue::Low // Active low + } else { + false + } + } + + fn is_clock_rising_edge(&self, last_clock: &mut PinValue) -> bool { + if let Some(clk_pin) = self.base.get_pin("clk") { + let current_clock = clk_pin.read(); + let rising_edge = *last_clock == PinValue::Low && current_clock == PinValue::High; + *last_clock = current_clock; + rising_edge + } else { + false + } + } + + pub(crate) fn decode_address(&self, bus_address: u64) -> Option { + // Check if address is in our range + let relative_address = bus_address & self.address_mask; + + if (bus_address & !self.address_mask) == self.base_address { + // Address matches our base + mask + let index = relative_address as usize; + if index < SIZE { + Some(index) + } else { + None + } + } else { + None + } + } + + fn start_read_operation(&mut self, address: u64) { + if let Some(index) = self.decode_address(address) { + self.current_operation = Some(RamOperation::Read(address)); + self.operation_cycles = 0; + println!("RAM {}: Starting read from address {:X} (index {})", + self.base.name, address, index); + } + } + + fn start_write_operation(&mut self, address: u64, data: u64) { + if let Some(index) = self.decode_address(address) { + self.current_operation = Some(RamOperation::Write(address, data)); + self.operation_cycles = 0; + println!("RAM {}: Starting write {:X} to address {:X} (index {})", + self.base.name, data, address, index); + } + } + + fn complete_read_operation(&mut self, address: u64) { + if let Some(index) = self.decode_address(address) { + let data = self.memory[index]; + self.output_buffer = Some(data); + println!("RAM {}: Read complete - data {:X} from address {:X}", + self.base.name, data, address); + } + self.current_operation = None; + } + + fn complete_write_operation(&mut self, address: u64, data: u64) { + if let Some(index) = self.decode_address(address) { + self.memory[index] = data & ((1 << DATA_WIDTH) - 1); // Mask to data width + println!("RAM {}: Write complete - data {:X} to address {:X}", + self.base.name, data, address); + } + self.current_operation = None; + } + + fn update_operation(&mut self) { + if let Some(operation) = self.current_operation { + self.operation_cycles += 1; + + match operation { + RamOperation::Read(addr) => { + if self.operation_cycles >= self.read_delay { + self.complete_read_operation(addr); + } + } + RamOperation::Write(addr, data) => { + if self.operation_cycles >= self.write_delay { + self.complete_write_operation(addr, data); + } + } + } + } + } + + fn handle_bus_signals(&mut self, bus_address: u64, last_clock: &mut PinValue) { + if !self.is_selected() { + // Not selected - high impedance and clear any pending operations + self.set_data_bus_high_z(); + self.current_operation = None; + return; + } + + let clock_rising_edge = self.is_clock_rising_edge(last_clock); + + if clock_rising_edge { + // On clock edge, check what operation is requested + if self.is_write_enabled() { + // Write operation + let data = self.read_data_bus(); + self.start_write_operation(bus_address, data); + } else if self.is_output_enabled() { + // Read operation + self.start_read_operation(bus_address); + } + } + + // Handle ongoing operations + self.update_operation(); + + // Output data if we have it and output is enabled + if self.is_output_enabled() { + if let Some(data) = self.output_buffer { + self.write_data_bus(data); + } else { + self.set_data_bus_high_z(); + } + } else { + self.set_data_bus_high_z(); + } + } +} + +impl +crate::component::Component for GenericRam +{ + fn name(&self) -> &str { + self.base.name() + } + + fn pins(&self) -> &std::collections::HashMap> { + self.base.pins() + } + + fn get_pin(&self, name: &str) -> Option> { + self.base.get_pin(name) + } + + fn update(&mut self) -> Result<(), String> { + let bus_address = self.read_address_bus(); + let mut last_clock = PinValue::Low; + + self.handle_bus_signals(bus_address, &mut last_clock); + Ok(()) + } + + fn run(&mut self) { + self.base.run(); + } + + fn stop(&mut self) { + self.base.stop(); + } +} + +// BusDevice implementation +impl +crate::bus::BusDevice for GenericRam +{ + fn update(&mut self) { + // Delegate to component update + let _ = Component::update(self); + } + + fn connect_to_bus(&mut self, bus_pins: Vec>) { + // Connect to bus pins - in a real system, this would connect address, data, and control pins + // This is simplified for the example + } + + fn get_address_range(&self) -> Option> { + Some(self.base_address..self.base_address + SIZE as u64) + } + + fn get_name(&self) -> &str { + todo!() + } +} \ No newline at end of file diff --git a/src/components/memory/generic_rom.rs b/src/components/memory/generic_rom.rs new file mode 100644 index 0000000..977a3d0 --- /dev/null +++ b/src/components/memory/generic_rom.rs @@ -0,0 +1,118 @@ +use std::collections::HashMap; +use std::ops::Range; +use std::sync::Arc; +use crate::{Component, Pin, PinValue}; +use crate::bus::BusDevice; + +pub struct GenericRom { + pub(crate) base: crate::component::BaseComponent, + memory: HashMap, // Generic memory storage + data_width: usize, + address_range: std::ops::Range, + pub latched_address: () +} + +impl GenericRom { + pub fn new(name: String, base_address: u64) -> Self { + let mut base = crate::component::BaseComponent::new(name); + + // Add data pins based on width + for i in 0..DATA_WIDTH { + base.add_pin(format!("data_{}", i), PinValue::HighZ, false); + } + + // Add control pins + base.add_pin("cs".to_string(), PinValue::Low, false); + base.add_pin("oe".to_string(), PinValue::Low, false); + + let address_range = base_address..base_address + SIZE as u64; + + Self { + base, + memory: HashMap::new(), + data_width: DATA_WIDTH, + address_range, + latched_address: (), + } + } + + pub fn load_data(&mut self, data: &[u64]) { + for (i, &word) in data.iter().enumerate() { + if i < SIZE { + self.memory.insert(self.address_range.start + i as u64, word); + } + } + } + pub fn is_selected(&self, address: u64) -> bool { + if let Some(cs_pin) = self.base.get_pin("cs") { + cs_pin.read() == PinValue::High && self.address_range.contains(&address) + } else { + false + } + } + fn get_current_address(&self) -> u64 { + // In a real system, this would read address from connected address pins + // For now, return a default address + 0 + } + + fn output_data(&self, data: u64) { + for i in 0..DATA_WIDTH { + if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { + let bit_value = (data >> i) & 1; + let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; + pin.write(pin_value, true); // Drive the pin + } + } + } + + fn set_data_pins_high_z(&self) { + for i in 0..DATA_WIDTH { + if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { + pin.write(PinValue::HighZ, false); // High impedance, not driving + } + } + } +} + +impl BusDevice for GenericRom { + fn update(&mut self) { + // Check if we're selected and output is enabled + if let (Some(cs_pin), Some(oe_pin)) = (self.base.get_pin("cs"), self.base.get_pin("oe")) { + let is_selected = cs_pin.read() == PinValue::High; + let output_enabled = oe_pin.read() == PinValue::Low; // Active low + + if is_selected && output_enabled { + // Read address from address pins (simplified - in real system, address would come from bus) + // For now, we'll use a dummy address - this would be connected to address pins in a real system + let address = self.get_current_address(); + + // Read data from ROM + let data = self.read(address); + + // Output data to data pins + self.output_data(data); + } else { + // Put data pins in high impedance when not selected + self.set_data_pins_high_z(); + } + } + } + + fn connect_to_bus(&mut self, bus_pins: Vec>) { + // Connect data pins + for (i, bus_pin) in bus_pins.iter().enumerate() { + if let Some(rom_pin) = self.base.get_pin(&format!("data_{}", i)) { + crate::connection::connect_pins(&rom_pin, bus_pin).unwrap(); + } + } + } + + fn get_address_range(&self) -> Option> { + Some(self.address_range.clone()) + } + + fn get_name(&self) -> &str { + todo!() + } +} \ No newline at end of file diff --git a/src/components/memory/intel_4001.rs b/src/components/memory/intel_4001.rs new file mode 100644 index 0000000..400713c --- /dev/null +++ b/src/components/memory/intel_4001.rs @@ -0,0 +1,56 @@ +use crate::components::memory::generic_rom::GenericRom; +use crate::{Component, PinValue}; +use crate::systems::intel_mcs_4::MCS4Phase; + +// 4001 ROM: 256 bytes, 4-bit data width +pub type Intel4001 = GenericRom<256, 4>; +impl Intel4001 { + fn update(&mut self) -> Result<(), String> { + // Read the current bus phase from the pins + let phase = self.detect_bus_phase(); + + match phase { + MCS4Phase::AddressLow => { + self.latch_address_low(); + } + MCS4Phase::AddressHigh => { + self.latch_address_high(); + if self.is_selected() { + self.prepare_data_output(); + } + } + MCS4Phase::DataRead => { + if self.is_selected() { + self.output_data(); + } + } + _ => { + // Put pins in high impedance + self.set_pins_high_z(); + } + } + + Ok(()) + } + fn detect_bus_phase(&self) -> MCS4Phase { + // Detect phase based on sync and other control signals + if let Some(sync_pin) = self.base.get_pin("pin_0") { + match sync_pin.read() { + PinValue::High => MCS4Phase::AddressLow, // Sync high = address phase + PinValue::Low => MCS4Phase::DataRead, // Sync low = data phase + _ => MCS4Phase::AddressLow, + } + } else { + MCS4Phase::AddressLow + } + } + fn latch_address_low(&mut self) { + let addr_low = self.read_nibble_from_pins(9..12); + self.latched_address = (self.latched_address & 0xFF0) | (addr_low as u16); + } + fn latch_address_high(&mut self) { + let addr_high = self.read_nibble_from_pins(9..12); + let addr_mid = self.read_nibble_from_pins(13..16); + self.latched_address = ((addr_mid as u16) << 8) | ((addr_high as u16) << 4) | (self.latched_address & 0x00F); + } +} \ No newline at end of file diff --git a/src/components/memory/intel_4002.rs b/src/components/memory/intel_4002.rs new file mode 100644 index 0000000..d7f1c4e --- /dev/null +++ b/src/components/memory/intel_4002.rs @@ -0,0 +1,51 @@ +use crate::components::memory::generic_ram::GenericRam; + +// 4002 RAM: 80 nibbles (4-bit words), 12-bit address bus +// Organized as 4 registers × 20 characters × 4 bits +pub type Intel4002 = GenericRam<80, 4, 12>; + +impl Intel4002 { + pub fn new_4002(name: String, base_address: u16) -> Self { + // 4002 has specific timing characteristics + let ram = Self::new( + name, + base_address as u64, + 2, // 2-cycle read access + 3 // 3-cycle write access + ); + + ram + } + + // 4002-specific methods + pub fn write_to_register_character(&mut self, register: u8, character: u8, value: u8) -> Result<(), String> { + if register >= 4 || character >= 20 { + return Err("Invalid register or character index".to_string()); + } + + let address = self.calculate_address(register, character); + let index = self.decode_address(address as u64) + .ok_or("Address out of range".to_string())?; + + self.memory[index] = (value & 0x0F) as u64; + Ok(()) + } + + pub fn read_from_register_character(&self, register: u8, character: u8) -> Result { + if register >= 4 || character >= 20 { + return Err("Invalid register or character index".to_string()); + } + + let address = self.calculate_address(register, character); + let index = self.decode_address(address as u64) + .ok_or("Address out of range".to_string())?; + + Ok(self.memory[index] as u8) + } + + fn calculate_address(&self, register: u8, character: u8) -> u16 { + // 4002 address calculation: + // Each register has 20 characters, each character is 4 bits + (register as u16 * 20) + (character as u16) + } +} \ No newline at end of file diff --git a/src/components/memory/mod.rs b/src/components/memory/mod.rs new file mode 100644 index 0000000..2bf13a2 --- /dev/null +++ b/src/components/memory/mod.rs @@ -0,0 +1,4 @@ +pub mod intel_4002; +pub mod intel_4001; +pub mod generic_rom; +mod generic_ram; \ No newline at end of file diff --git a/src/components/mod.rs b/src/components/mod.rs index 183ed32..c869d05 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,2 +1,3 @@ pub mod cpu; pub mod clock; +pub mod memory; diff --git a/src/lib.rs b/src/lib.rs index affc109..657e110 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod pin; pub mod connection; pub mod components; pub mod systems; +mod bus; // Re-export commonly used items for easier importing pub use component::{Component, BaseComponent}; diff --git a/src/main.rs b/src/main.rs index 44653df..7e31379 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ use rusty_emu::systems; fn main() { - systems::intel_mcs_4::emulation(); + systems::intel_mcs_4::run_fibonacci_calculator(); } \ No newline at end of file diff --git a/src/pin.rs b/src/pin.rs index 6e26068..7c8bbaf 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -7,45 +7,42 @@ pub enum PinValue { Low, HighZ, // High impedance (disconnected) } -// Pin structure with output enable + #[derive(Clone)] pub struct Pin { pub name: String, - pub value: Arc>, // Use RwLock for multiple readers - pub output_enable: Arc>, // Whether this pin is driving the line + pub value: Arc>, + pub output_enable: Arc>, pub connections: Arc>>>, } -// Component trait impl Pin { // Get the effective value considering all connected pins pub fn read(&self) -> PinValue { let connections = self.connections.lock().unwrap(); + let mut found_high_pin = false; + // Check if any connected pin is actively driving low for connected_pin in connections.iter() { let output_enable = connected_pin.output_enable.lock().unwrap(); let value = connected_pin.value.read().unwrap(); + if *output_enable && *value == PinValue::Low { return PinValue::Low; - } - } - - // If no one is driving low, check if anyone is driving high - for connected_pin in connections.iter() { - let output_enable = connected_pin.output_enable.lock().unwrap(); - let value = connected_pin.value.read().unwrap(); - - if *output_enable && *value == PinValue::High { - return PinValue::High; + } else if *output_enable && *value == PinValue::High { + found_high_pin = true; } } // If no one is driving, return HighZ - PinValue::HighZ + if found_high_pin { + PinValue::Low + } else { + PinValue::HighZ + } } - // Set this pin's value and output enable pub fn write(&self, value: PinValue, output_enable: bool) { *self.value.write().unwrap() = value; *self.output_enable.lock().unwrap() = output_enable; diff --git a/src/systems/intel_mcs_4.rs b/src/systems/intel_mcs_4.rs index a904b63..2fa0186 100644 --- a/src/systems/intel_mcs_4.rs +++ b/src/systems/intel_mcs_4.rs @@ -1,20 +1,46 @@ +use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; -use crate::component::Component; use crate::components::clock::generic_clock::GenericClock; use crate::components::cpu::intel_4004::Intel4004; -use crate::connection::connect_pins; - -pub fn emulation() { - println!("Starting MCS-4 emulation with pin state resolution..."); +use crate::components::memory::intel_4001::Intel4001; +use crate::components::memory::intel_4002::Intel4002; +use crate::{connect_pins, Component, PinValue}; +use crate::bus::GenericBus; +#[derive(Clone, Copy, PartialEq)] +pub enum MCS4Phase { + AddressLow, // Send address low nibble + AddressHigh, // Send address high nibble + middle + Instruction, // Fetch instruction + DataRead, // Read data + DataWrite, // Write data +} +pub fn run_fibonacci_calculator() { + println!("Starting MCS-4 Fibonacci Calculator..."); // Create components - let mut clock = GenericClock::new("sys_clock".to_string(), 2); // 2Hz clock - let mut cpu = Intel4004::new("cpu4004".to_string()); + let mut clock = GenericClock::new("CLK".parse().unwrap(), 1000); + let mut bus = GenericBus::new("BUS_MCS-4".parse().unwrap(), &[("B0", PinValue::HighZ),("B1", PinValue::HighZ),("B2", PinValue::HighZ),("B3", PinValue::HighZ)]); + let cpu = Arc::new(Mutex::new(Intel4004::new("cpu4004".to_string()))); - // Connect clock to CPU using the connection macro + // ROM 0: responds to addresses with high nibble 0x0 + let rom0 = Arc::new(Mutex::new(Intel4001::new("rom0".to_string(), 0x0))); + + // ROM 1: responds to addresses with high nibble 0x1 + let rom1 = Arc::new(Mutex::new(Intel4001::new("rom1".to_string(), 0x1))); + + // RAM bank 0: responds to addresses 0x400-0x47F + let ram0 = Arc::new(Mutex::new(Intel4002::new("ram0".to_string(), 0x400, 3, 2))); + + // Connect all devices to the bus + bus.connect_device(cpu.clone()); + bus.connect_device(rom0.clone()); + bus.connect_device(rom1.clone()); + bus.connect_device(ram0.clone()); + + // Connect clock to CPU if let (Some(clock_out), Some(cpu_clk)) = (clock.out(), cpu.clk()) { - connect_pins(&clock_out, &cpu_clk).expect("Connection could not be made!"); + connect_pins(&clock_out, &cpu_clk).expect("Clock connection failed"); println!("Connected clock to CPU"); } @@ -22,12 +48,58 @@ pub fn emulation() { clock.run(); cpu.run(); + load_fibonacci_program(rom0.clone()); + // Let it run for a while - thread::sleep(Duration::from_secs(5)); + thread::sleep(Duration::from_secs(10)); + + // Read results from RAM + let bus_lock = bus.lock().unwrap(); + let result1 = bus_lock.read(0x0200); // Fibonacci numbers stored here + let result2 = bus_lock.read(0x0201); + println!("Fibonacci results: {}, {}", result1, result2); // Stop components clock.stop(); cpu.stop(); - println!("Emulation completed"); + println!("Fibonacci calculation completed"); +} + +fn load_fibonacci_program(rom: Arc>) { + let mut rom_lock = rom.lock().unwrap(); + + // Simple Fibonacci program in 4004 machine code + let program = vec![ + 0x20, 0x00, // FIM R0,R1 = 0,0 (initialize) + 0x20, 0x11, // FIM R2,R3 = 1,1 (Fibonacci seed) + 0x20, 0x00, // FIM R4,R5 = 0,0 (counter) + + // Main loop + 0xA0, // LD R0 (load first number) + 0xA2, // LD R2 (load second number) + 0xF0, // ADD R0 (R0 + R2) + 0xE0, // WRM (store result) + 0xB0, // XCH R2 (move R2 to R0) + 0xA0, // LD R0 (get result) + 0xB2, // XCH R2 (update second number) + + // Increment counter + 0x64, // INC R4 + 0x10, 0x01, 0x00, // JCN (loop condition) + + // Store results + 0xA0, 0xE0, // LD R0, WRM (store final result 1) + 0xA2, 0xE1, // LD R2, WRM (store final result 2) + 0x00, // NOP + ]; + + // Load program into ROM + let mut rom_data = Vec::new(); + for &byte in &program { + rom_data.push(byte as u64); + } + rom_lock.load_data(&rom_data); + + println!("Loaded Fibonacci program into ROM"); } \ No newline at end of file