use rusty_emu::components::common::intel_400x::*; use rusty_emu::pin::PinValue; use rusty_emu::component::{BaseComponent, Component}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use std::collections::HashMap; // Mock component for testing trait implementations #[derive(Debug)] struct MockComponent { pins: std::collections::HashMap>>, } #[derive(Debug)] struct TestPin { value: PinValue, driver: Option<(String, PinValue)>, } type Pin = TestPin; impl Pin { fn new() -> Self { Self { value: PinValue::HighZ, driver: None, } } fn read(&self) -> PinValue { self.value } fn set_driver(&mut self, driver: Option, value: PinValue) { self.driver = driver.map(|name| (name, value)); self.value = value; } } impl MockComponent { fn new() -> Self { Self { pins: std::collections::HashMap::new(), } } fn add_pin(&mut self, name: &str, pin: Arc>) { self.pins.insert(name.to_string(), pin); } fn get_pin(&self, name: &str) -> Result>, String> { self.pins.get(name).cloned().ok_or_else(|| format!("Pin {} not found", name)) } } impl Component for MockComponent { fn name(&self) -> String { "MockComponent".to_string() } fn pins(&self) -> HashMap>> { HashMap::new() } fn get_pin(&self, name: &str) -> Result>, String> { // For testing, we'll return an error since we don't have real pins Err(format!("Mock pin not implemented for {}", name)) } fn update(&mut self) { // No-op for mock } fn run(&mut self) { // No-op for mock } fn stop(&mut self) { // No-op for mock } fn is_running(&self) -> bool { false } } impl Intel400xAddressHandling for MockComponent { fn get_base(&self) -> &BaseComponent { unimplemented!("MockComponent doesn't contain BaseComponent") } } #[cfg(test)] mod tests { use super::*; #[test] fn test_timing_constants() { // Test that timing constants have expected values assert_eq!(TimingConstants::DEFAULT_ACCESS_TIME, Duration::from_nanos(500)); assert_eq!(TimingConstants::FAST_ACCESS_TIME, Duration::from_nanos(200)); assert_eq!(TimingConstants::ADDRESS_SETUP, Duration::from_nanos(100)); assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200)); } #[test] fn test_assemble_full_address() { let mock = MockComponent::new(); // Create a mock trait implementation for testing struct TestAddressHandler { base: MockComponent, } impl TestAddressHandler { fn new() -> Self { Self { base: MockComponent::new(), } } } impl Intel400xAddressHandling for TestAddressHandler { fn get_base(&self) -> &BaseComponent { unimplemented!("TestAddressHandler doesn't contain BaseComponent") } } let handler = TestAddressHandler::new(); // Test address assembly with valid nibbles assert_eq!(handler.assemble_full_address(Some(0x0F), Some(0x23)), Some(0x0F23)); assert_eq!(handler.assemble_full_address(Some(0x12), Some(0x34)), Some(0x1234)); assert_eq!(handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x0000)); assert_eq!(handler.assemble_full_address(Some(0xFF), Some(0xFF)), Some(0xFFFF)); // Test with missing nibbles assert_eq!(handler.assemble_full_address(None, Some(0x23)), None); assert_eq!(handler.assemble_full_address(Some(0x0F), None), None); assert_eq!(handler.assemble_full_address(None, None), None); } #[test] fn test_timing_state_machine() { // Test state machine properties let idle_state = TimingState::Idle; let address_state = TimingState::AddressPhase; let wait_state = TimingState::WaitLatency; let drive_state = TimingState::DriveData; // Test state queries assert!(idle_state.is_idle()); assert!(!idle_state.is_address_phase()); assert!(!idle_state.is_waiting_latency()); assert!(!idle_state.is_driving_data()); assert!(!address_state.is_idle()); assert!(address_state.is_address_phase()); assert!(!address_state.is_waiting_latency()); assert!(!address_state.is_driving_data()); assert!(!wait_state.is_idle()); assert!(!wait_state.is_address_phase()); assert!(wait_state.is_waiting_latency()); assert!(!wait_state.is_driving_data()); assert!(!drive_state.is_idle()); assert!(!drive_state.is_address_phase()); assert!(!drive_state.is_waiting_latency()); assert!(drive_state.is_driving_data()); } #[test] fn test_memory_state_conversions() { // Test conversions between MemoryState and TimingState let mem_idle = MemoryState::Idle; let timing_idle: TimingState = mem_idle.into(); assert_eq!(timing_idle, TimingState::Idle); let mem_address = MemoryState::AddressPhase; let timing_address: TimingState = mem_address.into(); assert_eq!(timing_address, TimingState::AddressPhase); let mem_wait = MemoryState::WaitLatency; let timing_wait: TimingState = mem_wait.into(); assert_eq!(timing_wait, TimingState::WaitLatency); let mem_drive = MemoryState::DriveData; let timing_drive: TimingState = mem_drive.into(); assert_eq!(timing_drive, TimingState::DriveData); // Test reverse conversions let back_to_mem: MemoryState = timing_idle.into(); assert_eq!(back_to_mem, MemoryState::Idle); } #[test] fn test_ram_state_conversions() { // Test conversions between RamState and TimingState let ram_idle = RamState::Idle; let timing_idle: TimingState = ram_idle.into(); assert_eq!(timing_idle, TimingState::Idle); let ram_read = RamState::ReadData; let timing_drive: TimingState = ram_read.into(); assert_eq!(timing_drive, TimingState::DriveData); let ram_write = RamState::WriteData; let timing_drive: TimingState = ram_write.into(); assert_eq!(timing_drive, TimingState::DriveData); let ram_output = RamState::OutputPort; let timing_drive: TimingState = ram_output.into(); assert_eq!(timing_drive, TimingState::DriveData); } #[test] fn test_utils_create_driver_name() { assert_eq!(utils::create_driver_name("COMP1", "DATA"), "COMP1_DATA"); assert_eq!(utils::create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR"); assert_eq!(utils::create_driver_name("CPU", "CONTROL"), "CPU_CONTROL"); } #[test] fn test_clock_edge_detection_logic() { // Test edge detection logic without hardware dependencies // PHI1: Low -> High (rising edge) assert!(PinValue::High == PinValue::High && PinValue::Low == PinValue::Low); // PHI1: High -> Low (falling edge) assert!(PinValue::Low == PinValue::Low && PinValue::High == PinValue::High); // PHI2: Low -> High (rising edge) assert!(PinValue::High == PinValue::High && PinValue::Low == PinValue::Low); // PHI2: High -> Low (falling edge) assert!(PinValue::Low == PinValue::Low && PinValue::High == PinValue::High); // No edge cases assert!(PinValue::High == PinValue::High && PinValue::High == PinValue::Low); assert!(PinValue::Low == PinValue::Low && PinValue::Low == PinValue::High); } #[test] fn test_data_bus_bit_operations() { // Test bit manipulation for data bus operations let test_values = vec![ (0x00, [false, false, false, false]), (0x01, [true, false, false, false]), (0x02, [false, true, false, false]), (0x04, [false, false, true, false]), (0x08, [false, false, false, true]), (0x05, [true, false, true, false]), (0x0A, [false, true, false, true]), (0x0F, [true, true, true, true]), ]; for (value, expected_bits) in test_values { // Test bit extraction (reading from bus) for i in 0..4 { let bit_value = (value >> i) & 1; assert_eq!(bit_value == 1, expected_bits[i as usize]); } // Test bit setting (writing to bus) let mut reconstructed = 0; for i in 0..4 { if expected_bits[i as usize] { reconstructed |= 1 << i; } } assert_eq!(value, reconstructed); } } #[test] fn test_address_latching_logic() { // Test the address latching algorithm let mut high_nibble: Option = None; let mut low_nibble: Option = None; let mut full_address_ready = false; let mut address_latch_time: Option = None; let access_time = Duration::from_nanos(100); // Mock address handler for testing the algorithm struct TestAddressHandler { base: MockComponent, } impl TestAddressHandler { fn new() -> Self { Self { base: MockComponent::new(), } } } impl Intel400xAddressHandling for TestAddressHandler { fn get_base(&self) -> &BaseComponent { unimplemented!("TestAddressHandler doesn't contain BaseComponent") } } let handler = TestAddressHandler::new(); // First cycle: high nibble handler.handle_address_latching( 0x12, &mut high_nibble, &mut low_nibble, &mut full_address_ready, &mut address_latch_time, access_time, ); assert_eq!(high_nibble, Some(0x12)); assert_eq!(low_nibble, None); assert_eq!(full_address_ready, false); assert_eq!(address_latch_time, None); // Second cycle: low nibble handler.handle_address_latching( 0x34, &mut high_nibble, &mut low_nibble, &mut full_address_ready, &mut address_latch_time, access_time, ); assert_eq!(high_nibble, None); // Should be cleared assert_eq!(low_nibble, None); // Should be cleared assert_eq!(full_address_ready, true); assert!(address_latch_time.is_some()); // Verify the assembled address let assembled = handler.assemble_full_address(Some(0x12), Some(0x34)); assert_eq!(assembled, Some(0x1234)); } #[test] fn test_latency_timing_logic() { let access_time = Duration::from_nanos(100); // Test with no latch time (should return false) assert_eq!( Intel400xAddressHandling::handle_latency_wait( &MockComponent::new(), &None, access_time ), false ); // Test with recent latch time (should return false if not enough time elapsed) let recent_time = Some(Instant::now()); assert_eq!( Intel400xAddressHandling::handle_latency_wait( &MockComponent::new(), &recent_time, access_time ), false ); // Note: Testing with elapsed time would require sleeping or mocking time // This demonstrates the logic structure is correct } }