use parking_lot::Mutex; use rusty_emu::component::{BaseComponent, Component}; use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode}; use rusty_emu::pin::{Pin, PinValue}; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use std::time::Instant; // Mock component for testing trait implementations #[derive(Debug)] struct MockComponent {} impl MockComponent { fn new() -> Self { Self {} } } 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 } fn set_running(&mut self, _running: bool) { // No-op for mock } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } } impl Intel400xAddressHandling for MockComponent { fn get_base(&self) -> &BaseComponent { unimplemented!("MockComponent doesn't contain BaseComponent") } } #[cfg(test)] mod tests { use super::*; use std::thread; use std::time::Duration; #[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() { // Create a mock trait implementation for testing struct TestAddressHandler {} impl TestAddressHandler { fn new() -> Self { Self {} } } 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(0x03)), Some(0xF3) ); assert_eq!( handler.assemble_full_address(Some(0x02), Some(0x04)), Some(0x24) ); assert_eq!( handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x00) ); assert_eq!( handler.assemble_full_address(Some(0x0F), Some(0x0F)), Some(0xFF) ); // 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!(create_driver_name("COMP1", "DATA"), "COMP1_DATA"); assert_eq!(create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR"); assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL"); } #[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 {} impl TestAddressHandler { fn new() -> Self { Self {} } } 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(0x02), Some(0x04)); assert_eq!(assembled, Some(0x24)); } #[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 ); thread::sleep(Duration::from_micros(1)); // 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 ), true ); // Note: Testing with elapsed time would require sleeping or mocking time // This demonstrates the logic structure is correct } #[test] fn test_intel4001_integration_debugging() { println!("=== Intel 4001 Integration Test: Setup ==="); let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 1); // 1ns for fast testing rom.base.set_running(true); // Load test data: simple arithmetic sequence let test_data = vec![ 0x01, 0x02, 0x03, 0x04, 0x05, 0x10, 0x20, 0x30, 0x40, 0x50, 0xFF, ]; rom.load_rom_data(test_data.clone(), 0).unwrap(); // Verify test data loaded for (i, &data) in test_data.iter().enumerate() { assert_eq!(rom.read_rom(i as u8).unwrap(), data); } // Get pin references let sync_pin = rom.get_pin("SYNC").unwrap(); let cm_pin = rom.get_pin("CM").unwrap(); let ci_pin = rom.get_pin("CI").unwrap(); let reset_pin = rom.get_pin("RESET").unwrap(); let phi1_pin = rom.get_pin("PHI1").unwrap(); let phi2_pin = rom.get_pin("PHI2").unwrap(); let d0_pin = rom.get_pin("D0").unwrap(); let d1_pin = rom.get_pin("D1").unwrap(); let d2_pin = rom.get_pin("D2").unwrap(); let d3_pin = rom.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin.lock().set_settlement_time(Duration::from_nanos(0)); cm_pin.lock().set_settlement_time(Duration::from_nanos(0)); ci_pin.lock().set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .set_settlement_time(Duration::from_nanos(0)); phi1_pin.lock().set_settlement_time(Duration::from_nanos(0)); phi2_pin.lock().set_settlement_time(Duration::from_nanos(0)); d0_pin.lock().set_settlement_time(Duration::from_nanos(0)); d1_pin.lock().set_settlement_time(Duration::from_nanos(0)); d2_pin.lock().set_settlement_time(Duration::from_nanos(0)); d3_pin.lock().set_settlement_time(Duration::from_nanos(0)); // Initially, all data pins should be HighZ assert_eq!(d0_pin.lock().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().read(), PinValue::HighZ); // Test reset (RESET is active LOW) println!("=== Testing Reset ==="); { let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); assert_eq!(rom.get_io_mode(), IoMode::Input); // Release reset (set HIGH for normal operation) { let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } rom.update(); // Test memory read at address 0x00 println!("=== Memory Access Testing: Address 0x00 ==="); test_memory_read( &mut rom, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0x00, 0x01, ); // Test memory read at address 0xFF // Load test data at address 0xFF specifically // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time // The value 0x0F will be read as 0x0F (low nibble only) rom.load_rom_data(vec![0x0F], 0xFF).unwrap(); println!("=== Memory Access Testing: Address 0xFF ==="); test_memory_read( &mut rom, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0xFF, 0x0F, // 4-bit value (low nibble only) ); // Test I/O operations println!("=== I/O Integration Testing ==="); // Note: I/O operations on Intel 4001 require proper clock timing and control signals. // The handle_io_operation() method requires: // - CM (chip_select) = HIGH // - CI (io_select) = HIGH // - Proper clock edges to trigger the operation // For this test, we'll verify the direct I/O port access methods work correctly // since full I/O timing simulation would require more complex test setup. // Test direct I/O port write (using set_io_port API) rom.set_io_mode(IoMode::Output); rom.set_io_port(0, 0x05).unwrap(); assert_eq!(rom.get_io_port(0).unwrap(), 0x05); // Test I/O port read (using get_io_port API) rom.set_io_port(0, 0x0A).unwrap(); assert_eq!(rom.get_io_port(0).unwrap(), 0x0A); // Test that I/O direction is set to Output when we write assert_eq!(rom.get_io_direction(0).unwrap(), IoDirection::Output); // Debugging features println!("=== Debugging Features ==="); rom.debug_state_transitions("Integration Test"); // Pin state snapshots println!( "Pin states: SYNC: {:?}, CM: {:?}, CI: {:?}, PHI1: {:?}, PHI2: {:?}", sync_pin.lock().read(), cm_pin.lock().read(), ci_pin.lock().read(), phi1_pin.lock().read(), phi2_pin.lock().read() ); println!("Test completed successfully!"); } fn test_memory_read( rom: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, d0_pin: &Arc>, d1_pin: &Arc>, d2_pin: &Arc>, d3_pin: &Arc>, address: u8, expected_data: u8, ) { // Reset to known state first (RESET is active LOW) { let reset_pin = rom.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } rom.update(); { let reset_pin = rom.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } rom.update(); // Set SYNC=1, CM=1, CI=0 for memory read { let mut sync_guard = sync_pin.lock(); let mut cm_guard = cm_pin.lock(); let mut ci_guard = ci_pin.lock(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Start the component rom.base.set_running(true); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // Set high nibble on data bus rom.write_data_bus((address >> 4) & 0x0F); rom.update(); // PHI1 high to low: Complete address phase for high nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Set low nibble on data bus rom.write_data_bus(address & 0x0F); rom.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Check if we're in WaitLatency or DriveData state // With very small access times, we might transition directly to DriveData let state = rom.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Wait for latency if needed if state == TimingState::WaitLatency { // Get the actual access time from the ROM let access_time_ns = rom.get_access_time(); thread::sleep(Duration::from_nanos(access_time_ns + 10)); // Add small buffer rom.update(); // Update after waiting to check latency } // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = rom.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } rom.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::DriveData); // Wait a bit for data to be driven thread::sleep(Duration::from_nanos(10)); rom.update(); // Additional update to ensure data is driven on the bus // Check data on bus let data = rom.read_data_bus(); assert_eq!(data, expected_data); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Tri-state pins assert_eq!(d0_pin.lock().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().read(), PinValue::HighZ); } #[test] fn test_minimum_access_time() { // Test behavior at minimum access time (200ns) with comprehensive edge cases let mut chip = Intel4001::new_with_access_time("MIN_TIME_ROM".to_string(), 200); // 200ns minimum access time chip.base.set_running(true); // Get pin references let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let reset_pin = chip.get_pin("RESET").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); let d0_pin = chip.get_pin("D0").unwrap(); let d1_pin = chip.get_pin("D1").unwrap(); let d2_pin = chip.get_pin("D2").unwrap(); let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin.lock().set_settlement_time(Duration::from_nanos(0)); cm_pin.lock().set_settlement_time(Duration::from_nanos(0)); ci_pin.lock().set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .set_settlement_time(Duration::from_nanos(0)); phi1_pin.lock().set_settlement_time(Duration::from_nanos(0)); phi2_pin.lock().set_settlement_time(Duration::from_nanos(0)); d0_pin.lock().set_settlement_time(Duration::from_nanos(0)); d1_pin.lock().set_settlement_time(Duration::from_nanos(0)); d2_pin.lock().set_settlement_time(Duration::from_nanos(0)); d3_pin.lock().set_settlement_time(Duration::from_nanos(0)); // Test memory read at minimum timing // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time // The full 8-bit read would require M1/M2 cycles (not yet implemented) let address = 0x12; let expected_data = 0x04; // 4-bit value (low nibble only for single-cycle read) // Set up ROM data chip.load_rom_data(vec![expected_data], address).unwrap(); // Test memory read using the helper function test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u8, expected_data, ); // Verify state machine transitions correctly at minimum timing assert_eq!(chip.get_timing_state(), TimingState::Idle); // Test multiple consecutive reads at minimum timing // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time // The expected data must be a 4-bit value (0x00-0x0F) for addr in 0x10..0x15 { // Use 4-bit values that fit on the data bus let expected_data = (addr & 0x0F) as u8; // Low nibble only let test_data = vec![expected_data]; chip.load_rom_data(test_data, addr).unwrap(); test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, addr as u8, expected_data, ); } // Enhanced edge case testing for minimum access time // Test edge case: consecutive reads at minimum access time // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 for address in 0x00..0x10 { // Use addresses 0x00-0x0F where low nibble equals address let expected_data = (address & 0x0F) as u8; // 4-bit value let result1 = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, expected_data, ); assert_eq!( result1, expected_data, "Failed consecutive read 1 at address {} at minimum access time", address ); } // Test edge case: rapid address changes at minimum access time // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 let test_addresses = [0, 255, 128, 1, 254, 2, 253, 3]; for &address in &test_addresses { let expected_data = (address & 0x0F) as u8; // 4-bit value for data bus chip.load_rom_data(vec![expected_data], address).unwrap(); let result = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, expected_data, ); assert_eq!( result, expected_data, "Failed rapid address change test at address {} at minimum access time", address ); } // Test edge case: minimum access time with I/O operations // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 chip.load_rom_data(vec![0x05], 0x00).unwrap(); // Test pattern (4-bit value) chip.load_rom_data(vec![0x0A], 0xFF).unwrap(); // Another test pattern (4-bit value) // Test I/O pin behavior during minimum access time reads let io_pins = ["IO0", "IO1", "IO2", "IO3"]; // Set I/O pins to output mode for pin_name in &io_pins { let _io_pin = chip.get_pin(pin_name).unwrap(); // Note: In the actual implementation, I/O pin mode is controlled through the chip's I/O methods // For this test, we'll verify the I/O functionality through the chip's methods } // Test memory read while I/O operations are active let result = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0x00, 0x05, ); assert_eq!( result, 0x05, "Failed memory read with active I/O at minimum access time" ); // Test edge case: minimum access time with boundary addresses // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 let boundary_addresses = [0, 1, 254, 255]; for &address in &boundary_addresses { let expected_data = (address & 0x0F) as u8; // 4-bit value for data bus chip.load_rom_data(vec![expected_data], address).unwrap(); let result = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, expected_data, ); assert_eq!( result, expected_data, "Failed boundary address test at {} at minimum access time", address ); } // Test state machine transitions at minimum access time // Verify proper state progression through all phases // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time let mut test_chip = Intel4001::new_with_access_time("STATE_TEST_ROM".to_string(), 200); test_chip.base.set_running(true); test_chip.load_rom_data(vec![0x02], 0x10).unwrap(); // 4-bit value (low nibble only) // Get pins for state machine test let test_sync = test_chip.get_pin("SYNC").unwrap(); let test_cm = test_chip.get_pin("CM").unwrap(); let test_ci = test_chip.get_pin("CI").unwrap(); let test_phi1 = test_chip.get_pin("PHI1").unwrap(); let test_phi2 = test_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 test_sync .lock() .set_settlement_time(Duration::from_nanos(0)); test_cm.lock().set_settlement_time(Duration::from_nanos(0)); test_ci.lock().set_settlement_time(Duration::from_nanos(0)); test_phi1 .lock() .set_settlement_time(Duration::from_nanos(0)); test_phi2 .lock() .set_settlement_time(Duration::from_nanos(0)); // Initialize PHI2 to Low to ensure proper edge detection { let mut phi2_guard = test_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); // Manually step through the state machine to verify timing // Set control pins for memory read { let mut sync_guard = test_sync.lock(); let mut cm_guard = test_cm.lock(); let mut ci_guard = test_ci.lock(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::Idle); // Ensure PHI1 is low initially { let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); // Start address phase - first PHI1 cycle for high nibble { let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::AddressPhase); // Apply high nibble test_chip.write_data_bus(0x01); // High nibble test_chip.update(); // PHI1 falling edge - latch high nibble { let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::AddressPhase); // Still in address phase // Second PHI1 cycle for low nibble { let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); // Apply low nibble test_chip.write_data_bus(0x00); // Low nibble test_chip.update(); // PHI1 falling edge - latch low nibble and complete address { let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::WaitLatency); // Wait for minimum latency (200ns) thread::sleep(Duration::from_nanos(210)); // Slightly more than minimum // PHI2 high: Data phase { let mut phi2_guard = test_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::DriveData); // Verify data is driven correctly (4-bit value) let data = test_chip.read_data_bus(); assert_eq!(data, 0x02); // Complete cycle { let mut phi2_guard = test_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::Idle); // Test edge case: timing violations at minimum access time // Test what happens when we try to read too quickly let mut fast_chip = Intel4001::new_with_access_time("FAST_VIOLATION_ROM".to_string(), 200); fast_chip.base.set_running(true); fast_chip.load_rom_data(vec![0x09], 0x50).unwrap(); // Use 4-bit value (0x09) let fast_sync = fast_chip.get_pin("SYNC").unwrap(); let fast_cm = fast_chip.get_pin("CM").unwrap(); let fast_ci = fast_chip.get_pin("CI").unwrap(); let fast_phi1 = fast_chip.get_pin("PHI1").unwrap(); let fast_phi2 = fast_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 fast_sync .lock() .set_settlement_time(Duration::from_nanos(0)); fast_cm.lock().set_settlement_time(Duration::from_nanos(0)); fast_ci.lock().set_settlement_time(Duration::from_nanos(0)); fast_phi1 .lock() .set_settlement_time(Duration::from_nanos(0)); fast_phi2 .lock() .set_settlement_time(Duration::from_nanos(0)); // Initialize PHI2 to Low to ensure proper edge detection { let mut phi2_guard = fast_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Set up for memory read { let mut sync_guard = fast_sync.lock(); let mut cm_guard = fast_cm.lock(); let mut ci_guard = fast_ci.lock(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // First PHI1 cycle - latch high nibble { let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); // Apply high nibble fast_chip.write_data_bus(0x05); // High nibble of address 0x50 fast_chip.update(); // PHI1 falling edge - latch high nibble { let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Second PHI1 cycle - latch low nibble { let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); // Apply low nibble fast_chip.write_data_bus(0x00); // Low nibble of address 0x50 fast_chip.update(); // PHI1 falling edge - latch low nibble and complete address { let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // At this point, the state should be WaitLatency or DriveData let state = fast_chip.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = fast_chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } fast_chip.update(); // Try to read data - PHI2 rising edge triggers data driving if latency elapsed { let mut phi2_guard = fast_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); // Wait for proper timing and then verify data thread::sleep(Duration::from_nanos(210)); fast_chip.update(); // Now data should be valid (4-bit value) let data = fast_chip.read_data_bus(); assert_eq!(data, 0x09); } // Helper function for edge case memory read testing fn test_memory_read_edge_case( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, _d0_pin: &Arc>, _d1_pin: &Arc>, _d2_pin: &Arc>, _d3_pin: &Arc>, address: u16, expected_data: u8, ) -> u8 { // Load the expected data into ROM at the specified address chip.load_rom_data(vec![expected_data], address as usize) .unwrap(); // Reset to known state chip.perform_reset(); chip.update(); // Start the component chip.base.set_running(true); // Set control pins for memory read { let mut sync_guard = sync_pin.lock(); let mut cm_guard = cm_pin.lock(); let mut ci_guard = ci_pin.lock(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase for high nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for the address to be fully latched and state to transition thread::sleep(Duration::from_nanos(10)); chip.update(); // Check if we're in WaitLatency or DriveData state // With very small access times, we might transition directly to DriveData let state = chip.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Wait for latency if needed let current_state = chip.get_timing_state(); if current_state == TimingState::WaitLatency { // Get the actual access time from the ROM let access_time_ns = chip.get_access_time(); thread::sleep(Duration::from_nanos(access_time_ns + 10)); // Add small buffer chip.update(); // Update after waiting to check latency } // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); assert_eq!(chip.get_timing_state(), TimingState::DriveData); // Wait a bit for data to be driven thread::sleep(Duration::from_nanos(10)); chip.update(); // Additional update to ensure data is driven on the bus // Check data on bus let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); assert_eq!(chip.get_timing_state(), TimingState::Idle); data } }