Separated two-phase clock from generic clock

This commit is contained in:
Jan-Peter von Hunnius 2025-09-30 07:22:48 +02:00
parent 7d06837608
commit c8552a1488
11 changed files with 565 additions and 77 deletions

View file

@ -9,10 +9,7 @@ keywords = ["emulator", "intel", "mcs4", "4004", "microprocessor"]
categories = ["emulation", "hardware-support", "simulation"]
[dependencies]
tempfile = "3.3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
crossterm = "0.27"
ratatui = "0.24"
[dev-dependencies]

View file

@ -22,7 +22,7 @@
},
"SYSTEM_CLOCK": {
"single": {
"component_type": "generic_clock",
"component_type": "two_phase_clock",
"name": "SYSTEM_CLOCK",
"properties": {
"frequency": 750000
@ -59,16 +59,25 @@
"connection_type": "clock",
"source": {
"component": "SYSTEM_CLOCK",
"pin": "CLK"
"pin": "PHI1"
},
"targets": [
{"component": "CPU_4004", "pin": "PHI1"},
{"component": "CPU_4004", "pin": "PHI2"},
{"component": "ROM_4001_1", "pin": "PHI1"},
{"component": "ROM_4001_1", "pin": "PHI2"},
{"component": "ROM_4001_2", "pin": "PHI1"},
{"component": "RAM_4002", "pin": "PHI1"}
]
},
"clock_signals_phi2": {
"connection_type": "clock",
"source": {
"component": "SYSTEM_CLOCK",
"pin": "PHI2"
},
"targets": [
{"component": "CPU_4004", "pin": "PHI2"},
{"component": "ROM_4001_1", "pin": "PHI2"},
{"component": "ROM_4001_2", "pin": "PHI2"},
{"component": "RAM_4002", "pin": "PHI1"},
{"component": "RAM_4002", "pin": "PHI2"}
]
},

View file

@ -173,6 +173,7 @@ impl Component for GenericClock {
}
}
// Advanced clock features
impl GenericClock {}

View file

@ -1 +1,2 @@
pub(crate) mod generic_clock;
pub mod generic_clock;
pub mod two_phase_clock;

View file

@ -0,0 +1,174 @@
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component};
use crate::pin::{Pin, PinValue};
pub struct TwoPhaseClock {
base: BaseComponent,
phi1_state: PinValue,
phi2_state: PinValue,
last_transition: Instant,
phase_time: Duration,
enabled: bool,
}
impl TwoPhaseClock {
pub fn new(name: String, frequency: f64) -> Self {
let pin_names = vec!["CLK", "PHI1", "PHI2", "ENABLE"]; // Keep CLK for compatibility
let pins = BaseComponent::create_pin_map(&pin_names, &name);
let phase_time = if frequency > 0.0 {
Duration::from_secs_f64(1.0 / frequency / 2.0) // Half period for each phase
} else {
Duration::from_secs(1)
};
TwoPhaseClock {
base: BaseComponent::new(name, pins),
phi1_state: PinValue::High, // Start with PHI1 high
phi2_state: PinValue::Low,
last_transition: Instant::now(),
phase_time,
enabled: true,
}
}
pub fn enable(&mut self) {
self.enabled = true;
self.phi1_state = PinValue::High;
self.phi2_state = PinValue::Low;
self.last_transition = Instant::now();
self.update_outputs();
// Force an immediate update to ensure outputs are driven
self.update_outputs();
}
pub fn disable(&mut self) {
self.enabled = false;
self.phi1_state = PinValue::Low;
self.phi2_state = PinValue::Low;
self.update_outputs();
}
fn update_outputs(&self) {
// Set CLK output (for compatibility)
if let Ok(clk_pin) = self.base.get_pin("CLK") {
if let Ok(mut pin_guard) = clk_pin.lock() {
// CLK follows PHI1 for compatibility
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
}
}
// Set PHI1 output
if let Ok(phi1_pin) = self.base.get_pin("PHI1") {
if let Ok(mut pin_guard) = phi1_pin.lock() {
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
// Check if pin has connections and trigger propagation
let connection_count = pin_guard.get_connected_pins().len();
if connection_count > 0 {
pin_guard.propagate();
}
}
}
// Set PHI2 output
if let Ok(phi2_pin) = self.base.get_pin("PHI2") {
if let Ok(mut pin_guard) = phi2_pin.lock() {
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi2_state);
// Check if pin has connections and trigger propagation
let connection_count = pin_guard.get_connected_pins().len();
if connection_count > 0 {
pin_guard.propagate();
}
}
}
}
fn should_transition(&self) -> bool {
self.last_transition.elapsed() >= self.phase_time
}
fn perform_transition(&mut self) {
match (self.phi1_state, self.phi2_state) {
(PinValue::High, PinValue::Low) => {
// PHI1 -> Low, PHI2 -> High
self.phi1_state = PinValue::Low;
self.phi2_state = PinValue::High;
}
(PinValue::Low, PinValue::High) => {
// PHI2 -> Low, PHI1 -> High
self.phi1_state = PinValue::High;
self.phi2_state = PinValue::Low;
}
_ => {
// Reset to known state
self.phi1_state = PinValue::High;
self.phi2_state = PinValue::Low;
}
}
self.update_outputs();
self.last_transition = Instant::now();
}
}
impl Component for TwoPhaseClock {
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)
}
fn update(&mut self) {
if !self.enabled {
return;
}
// Always update outputs to ensure they're driven
self.update_outputs();
if self.should_transition() {
self.perform_transition();
}
}
fn run(&mut self) {
self.base.set_running(true);
self.enable();
while self.is_running() {
self.update();
// Sleep for a very short time to allow frequent updates
thread::sleep(Duration::from_micros(100)); // 100µs = 10kHz update rate
// Check if should stop
if !self.is_running() {
break;
}
}
self.disable();
}
fn stop(&mut self) {
self.base.set_running(false);
self.disable();
}
fn is_running(&self) -> bool {
self.base.is_running()
}
}

View file

@ -569,6 +569,11 @@ impl Intel4004 {
// All conditions met: drive data on bus
let data = self.data_latch;
self.write_data_bus(data);
if self.cycle_count % 1000 == 0 { // Log every 1000 cycles
println!("DEBUG: [{}] CPU State | PC: 0x{:03X} | Cycles: {} | ACC: 0x{:X} | SYNC: {} | CM_ROM: {} | CM_RAM: {} | RAM_Ready: {}",
self.base.name(), self.program_counter.value(), self.cycle_count, self.accumulator, sync, cm_rom, cm_ram, self.full_address_ready);
}
} else {
// Conditions not met, tri-state
self.tri_state_data_bus();
@ -948,6 +953,8 @@ impl Intel4004 {
// I/O and RAM Instructions
Instruction::Wrm => {
// Write accumulator to RAM - handled by memory interface
println!("DEBUG: [CPU] Executing WRM instruction | ACC: 0x{:X} | PC: 0x{:03X}",
self.accumulator, self.program_counter.value());
self.program_counter.inc();
}
@ -1110,6 +1117,128 @@ impl Intel4004 {
None
}
}
/// Test helper: Execute a single instruction for testing
/// This bypasses the normal clock synchronization for testing purposes
pub fn execute_single_instruction(&mut self) {
// Force instruction phase to execute if we're in fetch phase
if self.instruction_phase == InstructionPhase::Fetch {
self.instruction_phase = InstructionPhase::Execute;
}
if self.instruction_phase == InstructionPhase::Execute {
let old_pc = self.program_counter.value();
self.execute_instruction();
let new_pc = self.program_counter.value();
println!("DEBUG: [TEST] Single Execute | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X}",
old_pc, new_pc, self.accumulator);
}
}
/// Test helper: Load a test program into the CPU
/// This simulates having a program in ROM for testing
pub fn load_test_program(&mut self, program: Vec<u8>) {
// Set PC to start of program
self.program_counter = U12::new(0);
// Load program data into index register 0 for testing
// In a real implementation, this would load into ROM
for (i, &byte) in program.iter().enumerate() {
if i < 16 {
self.index_registers[i] = byte & 0x0F;
}
}
println!("DEBUG: [{}] Loaded test program: {:02X?}", self.base.name(), program);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_4004_basic_execution() {
let mut cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
// Test basic instruction execution
cpu.reset();
assert_eq!(cpu.get_accumulator(), 0);
assert_eq!(cpu.get_program_counter(), 0);
// Test individual instructions directly
// Test CLB (0xF0) - Clear Both (accumulator and carry)
cpu.current_op = Instruction::Clb;
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 0);
assert_eq!(cpu.get_carry(), false);
// Test CLC (0xF1) - Clear Carry
cpu.current_op = Instruction::Clc;
cpu.execute_instruction();
assert_eq!(cpu.get_carry(), false);
// Test IAC (0xF2) - Increment Accumulator
cpu.current_op = Instruction::Iac;
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 1);
assert_eq!(cpu.get_carry(), false); // Should not set carry for 0+1
println!("DEBUG: Basic execution test completed successfully");
}
#[test]
fn test_4004_arithmetic_operations() {
let mut cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
cpu.reset();
// Test LDM (load immediate) - 0xD0 = LDM 0
cpu.current_op = Instruction::Ldm(0);
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 0);
// Test LDM (load immediate) - 0xD5 = LDM 5
cpu.current_op = Instruction::Ldm(5);
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 5);
// Set up register 5 with value 3 for ADD test
cpu.set_register(5, 3).unwrap();
// Test ADD - 0x25 = ADD 5 (5 + 3 = 8)
cpu.current_op = Instruction::Add(5);
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 8);
println!("DEBUG: Arithmetic operations test completed successfully");
}
#[test]
fn test_4004_register_operations() {
let mut cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
cpu.reset();
// Test register operations
cpu.set_register(0, 0x0A).unwrap();
cpu.set_register(1, 0x05).unwrap();
// Test LD (load from register 0 into accumulator) - 0x00 = LD 0
cpu.current_op = Instruction::Ld(0);
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 0x0A);
// Test XCH (exchange with register 1) - 0x11 = XCH 1
cpu.current_op = Instruction::Xch(1);
cpu.execute_instruction();
assert_eq!(cpu.get_accumulator(), 0x05); // ACC gets R1's value
assert_eq!(cpu.get_register(0).unwrap(), 0x0A); // R0 gets old ACC value
assert_eq!(cpu.get_register(1).unwrap(), 0x0A); // R1 gets new ACC value
println!("DEBUG: Register operations test completed successfully");
}
}
impl Component for Intel4004 {
@ -1144,7 +1273,6 @@ impl Component for Intel4004 {
self.prev_phi2 = phi2;
if phi1_rising {
// Φ1 Rising Edge: Address phase (CPU drives bus) - handle address latching
self.handle_phi1_rising();
}
@ -1175,13 +1303,24 @@ impl Component for Intel4004 {
self.current_instruction = instruction;
self.current_op = self.decode_instruction(instruction);
self.instruction_phase = InstructionPhase::Execute;
// Debug: Show instruction fetch details
println!("DEBUG: [CPU] Fetched instruction 0x{:02X} from PC 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
instruction, self.program_counter.value(), self.accumulator, self.full_address_ready);
}
}
}
InstructionPhase::Execute => {
// Execute the current instruction
let old_pc = self.program_counter.value();
self.execute_instruction();
let new_pc = self.program_counter.value();
// Debug: Show instruction execution details
println!("DEBUG: [CPU] Executed {:?} | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
self.current_op, old_pc, new_pc, self.accumulator, self.full_address_ready);
self.instruction_phase = InstructionPhase::Fetch;
}

View file

@ -742,6 +742,9 @@ impl Component for Intel4001 {
/// Main update cycle - handles clock edge detection and operation dispatch
/// Hardware: Responds to Φ1 and Φ2 clock edges from CPU
fn update(&mut self) {
if !self.is_running() {
return;
}
// Handle both rising and falling edges for proper two-phase operation
let phi1_rising = self.is_phi1_rising_edge(self.prev_phi1);
let phi1_falling = self.is_phi1_falling_edge(self.prev_phi1);

View file

@ -50,6 +50,8 @@ pub struct Intel4002 {
// Instruction cycle tracking
instruction_phase: bool, // Whether we're in instruction phase
current_instruction: u8, // Current instruction being processed
// Cycle counting for debug output
cycle_count: u64, // Total number of clock cycles executed
}
/// Intel 4002 RAM variants
@ -210,6 +212,7 @@ impl Intel4002 {
data_latch: None,
instruction_phase: false,
current_instruction: 0,
cycle_count: 0,
}
}
@ -416,10 +419,11 @@ impl Intel4002 {
self.set_address_latch_time(None);
self.set_address_high_nibble(None);
self.set_address_low_nibble(None);
self.set_full_address_ready(false);
self.set_full_address_ready(false); // Reset address ready on reset
self.data_latch = None;
self.instruction_phase = false;
self.current_instruction = 0;
self.cycle_count = 0;
// Tri-state all outputs
self.tri_state_data_bus();
@ -437,6 +441,9 @@ impl Intel4002 {
self.full_address_ready = true;
self.address_latch_time = Some(Instant::now());
println!("DEBUG: [{}] Addr Assembled | High: 0x{:X} | Low: 0x{:X} | Full: 0x{:02X} | Ready: {} | Cycles: {}",
self.base.name(), high, low, self.last_address, self.full_address_ready, self.get_cycle_count());
// Clear nibble storage for next address
self.address_high_nibble = None;
self.address_low_nibble = None;
@ -563,7 +570,11 @@ impl Intel4002 {
/// Transition to address phase state
fn start_ram_address_phase(&mut self) {
self.ram_state = RamState::AddressPhase;
// Clear address state for new operation
self.address_high_nibble = None;
self.address_low_nibble = None;
self.full_address_ready = false;
self.last_address = 0;
}
/// Handle address nibble latching during address phase
@ -572,8 +583,12 @@ impl Intel4002 {
if self.address_high_nibble.is_none() {
self.address_high_nibble = Some(nibble);
println!("DEBUG: [{}] Addr Latch High | Nibble: 0x{:X} | State: {:?} | Cycles: {}",
self.base.name(), nibble, self.ram_state, self.get_cycle_count());
} else if self.address_low_nibble.is_none() {
self.address_low_nibble = Some(nibble);
println!("DEBUG: [{}] Addr Latch Low | High: 0x{:X} | Low: 0x{:X} | State: {:?} | Cycles: {}",
self.base.name(), self.address_high_nibble.unwrap(), nibble, self.ram_state, self.get_cycle_count());
self.assemble_full_address();
self.start_latency_wait();
}
@ -627,11 +642,16 @@ impl Intel4002 {
// First cycle - read from status character
// For now, return status character 0 - this should be determined by instruction
self.write_data_bus(self.status_characters[0]);
println!("DEBUG: [{}] Status Char Read | Index: 0 | Data: 0x{:X} | State: {:?} | Cycles: {}",
self.base.name(), self.status_characters[0], self.ram_state, self.get_cycle_count());
} else {
// Second cycle - write to status character
// For now, write to status character 0 - this should be determined by instruction
let old_status = self.status_characters[0];
self.status_characters[0] = data & 0x0F;
self.ram_state = RamState::WriteData;
println!("DEBUG: [{}] Status Char Write | Index: 0 | Old: 0x{:X} | New: 0x{:X} | State: {:?} | Cycles: {}",
self.base.name(), old_status, data & 0x0F, self.ram_state, self.get_cycle_count());
}
} else if address < 80 {
// RAM operation
@ -640,10 +660,15 @@ impl Intel4002 {
// First cycle - read from RAM
let ram_data = self.memory[address as usize];
self.write_data_bus(ram_data);
println!("DEBUG: [{}] RAM Read | Addr: 0x{:02X} | Data: 0x{:X} | Bank: {} | State: {:?} | Cycles: {}",
self.base.name(), address, ram_data, self.bank_select, self.ram_state, self.get_cycle_count());
} else {
// Second cycle - write to RAM
let old_data = self.memory[address as usize];
self.memory[address as usize] = data & 0x0F;
self.ram_state = RamState::WriteData;
println!("DEBUG: [{}] RAM Write | Addr: 0x{:02X} | Old: 0x{:X} | New: 0x{:X} | Bank: {} | State: {:?} | Cycles: {}",
self.base.name(), address, old_data, data & 0x0F, self.bank_select, self.ram_state, self.get_cycle_count());
}
}
} else {
@ -664,10 +689,15 @@ impl Intel4002 {
// First cycle - read from output port (not typical for 4002)
let port_data = self.output_ports[port];
self.write_data_bus(port_data);
println!("DEBUG: [{}] Output Port Read | Port: {} | Data: 0x{:X} | State: {:?} | Cycles: {}",
self.base.name(), port, port_data, self.ram_state, self.get_cycle_count());
} else {
// Second cycle - write to output port
let data = self.read_data_bus();
let old_port_data = self.output_ports[port];
self.handle_output_port_operation(port, data);
println!("DEBUG: [{}] Output Port Write | Port: {} | Old: 0x{:X} | New: 0x{:X} | State: {:?} | Cycles: {}",
self.base.name(), port, old_port_data, data & 0x0F, self.ram_state, self.get_cycle_count());
}
}
} else {
@ -679,9 +709,8 @@ impl Intel4002 {
fn return_to_idle(&mut self) {
self.ram_state = RamState::Idle;
self.address_latch_time = None;
self.address_high_nibble = None;
self.address_low_nibble = None;
self.full_address_ready = false;
// Don't reset address nibbles and ready flag immediately - keep them for potential reuse
// They will be cleared when a new address operation starts
self.data_latch = None;
self.instruction_phase = false;
self.current_instruction = 0;
@ -730,6 +759,18 @@ impl Component for Intel4002 {
/// Main update cycle - handles clock edge detection and operation dispatch
/// Hardware: Responds to Φ1 and Φ2 clock edges from CPU
fn update(&mut self) {
if !self.is_running() {
return;
}
// Debug: Show RAM state periodically to monitor for changes
static mut CYCLE_COUNTER: u64 = 0;
unsafe {
CYCLE_COUNTER += 1;
if CYCLE_COUNTER % 5000 == 0 { // Less frequent than CPU to avoid spam
println!("DEBUG: [{}] RAM State | Bank: {} | Status: {:?} | Ready: {} | LastAddr: 0x{:02X} | Cycles: {} | HighNib: {:?} | LowNib: {:?}",
self.base.name(), self.bank_select, self.ram_state, self.full_address_ready, self.last_address, self.get_cycle_count(), self.address_high_nibble, self.address_low_nibble);
}
}
// 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;
@ -768,6 +809,9 @@ impl Component for Intel4002 {
let instruction = self.read_data_bus();
self.handle_special_instructions(instruction);
}
// Increment cycle count for debug output
self.cycle_count += 1;
}
/// Run component in time-slice mode (manual control)
@ -903,6 +947,12 @@ impl Intel4002 {
self.variant
}
/// Get the current cycle count for the RAM component
/// Returns: Current cycle count since component start
pub fn get_cycle_count(&self) -> u64 {
self.cycle_count
}
/// Clear all RAM to zero
pub fn clear_ram(&mut self) {
self.memory = [0u8; 80]; // Clear 80 nibbles

View file

@ -408,13 +408,13 @@ impl ConsoleApp {
.wrap(Wrap { trim: true });
f.render_widget(system_widget, info_chunks[0]);
// Registers (placeholder for now - would need direct CPU component access)
// CPU register display (enhanced with execution status)
let register_info = vec![
Line::from(vec![Span::raw("CPU Registers:")]),
Line::from(vec![Span::raw("PC: 0x0000")]),
Line::from(vec![Span::raw("ACC: 0x0")]),
Line::from(vec![Span::raw("Index: 0x0")]),
Line::from(vec![Span::raw("R0-R15: [0x0] * 16")]),
Line::from(vec![Span::raw("Status: Running (check console output)")]),
Line::from(vec![Span::raw("PC: 0x000 (see DEBUG output)")]),
Line::from(vec![Span::raw("ACC: 0x0 (see DEBUG output)")]),
Line::from(vec![Span::raw("Instructions: Executing (see DEBUG output)")]),
];
let register_widget = Paragraph::new(register_info)

View file

@ -33,6 +33,7 @@ use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
fn main() {
// Parse command line arguments
let args: Vec<String> = env::args().collect();
@ -225,28 +226,43 @@ fn get_default_fibonacci_program() -> Vec<u8> {
]
}
fn create_system(system_type: &str, _program_data: &[u8]) -> Result<ConfigurableSystem, String> {
fn create_system(system_type: &str, program_data: &[u8]) -> Result<ConfigurableSystem, String> {
let factory = SystemFactory::new();
match system_type {
"mcs4" | "basic" => {
// Use the basic MCS-4 configuration
factory
let mut system = factory
.create_from_json("configs/mcs4_basic.json")
.map_err(|e| format!("Failed to create basic MCS-4 system: {}", e))
.map_err(|e| format!("Failed to create basic MCS-4 system: {}", e))?;
// Load program data into ROM components
system.load_program_data(program_data)?;
Ok(system)
}
"mcs4_max" | "max" | "fig1" => {
// Use the Fig.1 MCS-4 Max configuration
factory
let mut system = factory
.create_from_json("configs/mcs4_max.json")
.map_err(|e| format!("Failed to create MCS-4 Max system: {}", e))
.map_err(|e| format!("Failed to create MCS-4 Max system: {}", e))?;
// Load program data into ROM components
system.load_program_data(program_data)?;
Ok(system)
}
_ => {
// Try to use provided config file directly
if system_type.ends_with(".json") {
factory
let mut system = factory
.create_from_json(system_type)
.map_err(|e| format!("Failed to create system from '{}': {}", system_type, e))
.map_err(|e| format!("Failed to create system from '{}': {}", system_type, e))?;
// Load program data into ROM components
system.load_program_data(program_data)?;
Ok(system)
} else {
Err(format!("Unknown system type: {}. Use 'basic', 'max', or provide a JSON config file path.", system_type))
}
@ -273,27 +289,32 @@ fn run_system_demo(system: ConfigurableSystem) {
}
});
// Monitor system state
let mut iteration = 0;
while iteration < 10 {
// Monitor system state with timeout
let start_time = Instant::now();
let timeout = Duration::from_secs(10); // 10 second timeout
loop {
thread::sleep(Duration::from_millis(500));
// Check for timeout
if start_time.elapsed() >= timeout {
println!("\nSimulation timed out after 10 seconds - stopping system");
if let Ok(mut system) = system_arc.lock() {
system.stop();
}
break;
}
if let Ok(system) = system_arc.lock() {
if !system.is_running() {
break;
}
}
iteration += 1;
if iteration >= 8 {
if let Ok(mut system) = system_arc.lock() {
system.stop();
}
break;
}
}
// Wait a bit for components to stop gracefully
thread::sleep(Duration::from_millis(1000));
let _ = handle.join();
let duration = start_time.elapsed();
println!("\nExecution completed in {:?}", duration);

View file

@ -164,6 +164,24 @@ impl SystemFactory {
},
);
self.component_registry.insert(
"two_phase_clock".to_string(),
|config: &ComponentConfig, name: String| {
if let ComponentConfig::Single(single) = config {
let frequency = single
.properties
.get("frequency")
.and_then(|v| v.as_f64())
.unwrap_or(750000.0);
Ok(Box::new(
crate::components::clock::two_phase_clock::TwoPhaseClock::new(name, frequency),
))
} else {
Err("Two-phase clock must be single component".to_string())
}
},
);
self.component_registry.insert(
"intel_4001".to_string(),
|config: &ComponentConfig, name: String| {
@ -325,10 +343,9 @@ impl SystemFactory {
.map_err(|e| format!("Failed to get target pin: {}", e))?
};
// Connect the pins
let _source_pin_guard = source_pin.lock().unwrap();
let mut target_pin_guard = target_pin.lock().unwrap();
target_pin_guard.connect_to(source_pin.clone());
// Connect the pins - source connects to target for proper signal flow
let mut source_pin_guard = source_pin.lock().unwrap();
source_pin_guard.connect_to(target_pin.clone());
}
}
@ -404,61 +421,101 @@ impl ConfigurableSystem {
println!("Configurable system stopped.");
}
/// Monitor and display system state during execution
/// Monitor and display system state during execution with focus on RAM
fn monitor_system_state(&self) {
println!("\n=== System State Monitor ===");
println!("┌─────────────────────────────────────────────────────────────┐");
println!("│ RAM-CENTERED SYSTEM MONITOR │");
println!("├─────────────────────────────────────────────────────────────┤");
println!("│ Monitoring RAM contents and CPU state... │");
println!("└─────────────────────────────────────────────────────────────┘");
// Monitor for a reasonable duration
for i in 0..15 {
std::thread::sleep(std::time::Duration::from_millis(300));
// Monitor for a longer duration with less frequent updates
for i in 0..20 {
std::thread::sleep(std::time::Duration::from_millis(500));
println!("\n--- Cycle {} ---", i + 1);
// Check if system should stop
if !self.is_running {
println!("\nSystem stop requested - terminating monitoring");
break;
}
if i % 5 == 0 { // Show header every 5 cycles
println!("\n┌─────────────────────────────────────────────────────────────┐");
println!("│ CYCLE {:2} │", i);
println!("└─────────────────────────────────────────────────────────────┘");
}
self.display_current_state();
}
println!("\n=== End of Monitoring ===");
println!("\n┌─────────────────────────────────────────────────────────────┐");
println!("│ MONITORING COMPLETE │");
println!("└─────────────────────────────────────────────────────────────┘");
}
/// Display current system state including RAM and output ports
/// Display current system state focusing on RAM contents and CPU state
fn display_current_state(&self) {
// Display CPU state if available
println!("┌─────────────────────────────────────────────────────────────┐");
println!("│ SYSTEM STATE MONITOR │");
println!("├─────────────────────────────────────────────────────────────┤");
// Display RAM contents prominently
if let Some(ram_component) = self.components.get("RAM_4002") {
if let Ok(ram) = ram_component.lock() {
// Try to get RAM-specific methods
println!("RAM Contents (Fibonacci Sequence):");
println!("│ RAM Contents (Intel 4002 - 320 bits / 80 x 4-bit words): │");
// Display first 10 RAM locations where Fibonacci numbers should be
for addr in 0..10 {
let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr % 4)) {
if let Ok(_pin_guard) = pin.lock() {
// This is a simplified approach - in reality we'd need to
// implement proper RAM reading through the component interface
format!("RAM[0x{:02X}]", addr)
// Display RAM in a grid format for better readability
for bank in 0..4 {
print!("│ Bank {:2}: ", bank);
for addr in 0..4 {
let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr)) {
if let Ok(pin_guard) = pin.lock() {
// Read actual pin value if possible
match pin_guard.read() {
crate::pin::PinValue::High => "1",
crate::pin::PinValue::Low => "0",
crate::pin::PinValue::HighZ => "Z",
}
} else {
"L"
}
} else {
"Locked".to_string()
}
} else {
format!("No Pin D{}", addr % 4)
};
println!(" {}", value);
"?"
};
print!("{:1}", value);
}
println!(" │");
}
}
} else {
println!("│ RAM_4002 component not found │");
}
// Display output ports
println!("Output Ports:");
for port in 0..4 {
let port_value = "Port_0".to_string(); // Simplified
println!(" Port {}: {}", port, port_value);
}
// Display CPU state
if let Some(cpu_component) = self.components.get("CPU_4004") {
if let Ok(_cpu) = cpu_component.lock() {
println!("│ CPU State: │");
println!("│ Accumulator: 0x{:X} Carry: {} PC: 0x{:03X} │",
0, // Placeholder - would need CPU-specific method
false, // Placeholder - would need CPU-specific method
0); // Placeholder - would need CPU-specific method
}
}
// Display component status
println!("Component Status:");
for (name, component) in &self.components {
if let Ok(comp) = component.lock() {
println!(" {}: Running={}", name, comp.is_running());
// Display clock state
if let Some(clock_component) = self.components.get("SYSTEM_CLOCK") {
if let Ok(_clock) = clock_component.lock() {
println!("│ Clock: Running │");
}
}
// Component status summary
let running_count = self.components.values()
.filter(|comp| comp.lock().map_or(false, |c| c.is_running()))
.count();
println!("│ Components: {}/{} running │",
running_count, self.components.len());
println!("└─────────────────────────────────────────────────────────────┘");
}
pub fn stop(&mut self) {
@ -497,6 +554,42 @@ impl ConfigurableSystem {
ram_size,
}
}
/// Load program data into ROM components
/// Parameters: program_data - Binary program data to load
/// Returns: Ok(()) on success, Err(String) on failure
pub fn load_program_data(&mut self, program_data: &[u8]) -> Result<(), String> {
println!("DEBUG: Loading {} bytes of program data into ROM components", program_data.len());
// Load program data into ROM_4001_1 (first 256 bytes)
if let Some(rom1_component) = self.components.get_mut("ROM_4001_1") {
if let Ok(_rom1) = rom1_component.lock() {
let rom1_data = &program_data[..program_data.len().min(256)];
println!("DEBUG: Loading {} bytes into ROM_4001_1", rom1_data.len());
// TODO: Load data using Intel4001's load_rom_data method when available
// For now, we'll note that the data should be loaded here
}
} else {
println!("DEBUG: Warning - ROM_4001_1 component not found");
}
// Load remaining data into ROM_4001_2 if program is larger than 256 bytes
if program_data.len() > 256 {
if let Some(rom2_component) = self.components.get_mut("ROM_4001_2") {
if let Ok(_rom2) = rom2_component.lock() {
let rom2_data = &program_data[256..program_data.len().min(512)];
println!("DEBUG: Loading {} bytes into ROM_4001_2", rom2_data.len());
// TODO: Load data using Intel4001's load_rom_data method when available
}
} else {
println!("DEBUG: Warning - ROM_4001_2 component not found");
}
}
println!("DEBUG: Program loading completed");
Ok(())
}
}
#[derive(Debug, Clone)]