//! Intel 4002 RAM Unified State Machine Diagnostic Test //! //! This test isolates Intel 4002 RAM unified state machine to verify that it correctly //! progresses through all states: Idle → AddressPhase → WaitLatency → ReadData/WriteData/OutputPort → Idle //! //! The test focuses on unified state machine progression without involving other system components. use std::thread; use std::time::Duration; use rusty_emu::component::Component; use rusty_emu::components::common::intel_400x::{ Intel400xTimingState, Intel400xUnifiedState, TimingState, }; use rusty_emu::components::memory::intel_4002::{Intel4002, PendingOperation}; use rusty_emu::pin::PinValue; /// Test helper to create a test RAM with minimal setup fn create_test_ram() -> Intel4002 { // Create RAM with 1ns access time for fast testing let mut ram = Intel4002::new_with_access_time("TEST_RAM".to_string(), 1); // Initialize RAM with test data using write_ram_direct for (i, &value) in [0x0A, 0x0B, 0x0C, 0x0D, 0x0E].iter().enumerate() { ram.write_ram_direct(i as u8, value).unwrap(); } // Start the component so it can process updates ram.base.set_running(true); // Initialize clock pins to Low to establish known state for edge detection let phi1_pin = ram.get_pin("PHI1").unwrap(); let phi2_pin = ram.get_pin("PHI2").unwrap(); { let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Process initial state (this initializes prev_phi1/prev_phi2 for edge detection) ram.update(); ram } /// Test helper to wait for pin settlement time fn wait_for_pin_settlement() { thread::sleep(Duration::from_nanos(10)); // 10ns settlement time } /// Test helper to set control pins for RAM operation /// RAM requires: SYNC=1, CM=1 (chip_select), CI=1 (io_select for RAM vs ROM) fn setup_ram_operation(ram: &Intel4002, sync_high: bool, cm_high: bool) { let sync_pin = ram.get_pin("SYNC").unwrap(); let cm_pin = ram.get_pin("CM").unwrap(); let ci_pin = ram.get_pin("CI").unwrap(); // Set control pins { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver( Some("TEST".to_string()), if sync_high { PinValue::High } else { PinValue::Low }, ); cm_guard.set_driver( Some("TEST".to_string()), if cm_high { PinValue::High } else { PinValue::Low }, ); // CI must be HIGH for RAM operations (distinguishes RAM from ROM) ci_guard.set_driver(Some("TEST".to_string()), PinValue::High); } wait_for_pin_settlement(); } /// Test helper to set data bus value fn set_data_bus(ram: &Intel4002, value: u8) { let nibble = value & 0x0F; // Only lower 4 bits for i in 0..4 { if let Ok(pin) = ram.get_pin(&format!("D{}", i)) { if let Ok(mut pin_guard) = pin.lock() { let bit_value = (nibble >> i) & 1; let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; pin_guard.set_driver(Some("TEST".to_string()), pin_value); } } } wait_for_pin_settlement(); } /// Test helper to clear data bus drivers fn clear_data_bus_drivers(ram: &Intel4002) { for i in 0..4 { if let Ok(pin) = ram.get_pin(&format!("D{}", i)) { if let Ok(mut pin_guard) = pin.lock() { pin_guard.remove_driver("TEST"); } } } wait_for_pin_settlement(); } /// Test helper to generate a clock edge fn generate_clock_edge(ram: &mut Intel4002, phi1_high: bool, phi2_high: bool) { let phi1_pin = ram.get_pin("PHI1").unwrap(); let phi2_pin = ram.get_pin("PHI2").unwrap(); // Set clock pins { let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock().unwrap(); phi1_guard.set_driver( Some("TEST".to_string()), if phi1_high { PinValue::High } else { PinValue::Low }, ); phi2_guard.set_driver( Some("TEST".to_string()), if phi2_high { PinValue::High } else { PinValue::Low }, ); } wait_for_pin_settlement(); // Update to process: edge ram.update(); } /// Test helper to generate a complete clock cycle fn generate_clock_cycle(ram: &mut Intel4002) { // Start with both clocks low generate_clock_edge(ram, false, false); // Φ1 rising edge generate_clock_edge(ram, true, false); // Φ1 falling edge generate_clock_edge(ram, false, false); // Φ2 rising edge generate_clock_edge(ram, false, true); // Φ2 falling edge generate_clock_edge(ram, false, false); } /// Test RAM read operation state progression /// /// Note: This test verifies the state machine behavior through direct state manipulation /// rather than through pin-based triggering. The Intel 4002 implementation processes /// events through process_event_direct() which doesn't call handle_component_event(), /// so chip select via pins doesn't work the same way as Intel 4001. /// /// Address latching happens on Φ1 falling edge (after data is stable). #[test] fn test_ram_read_state_progression() { println!("=== Testing RAM Read Operation State Progression ==="); let mut ram = create_test_ram(); // Initially should be in idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("Initial state: {:?}", ram.get_timing_state()); // Set operation type to Read ram.set_pending_operation(PendingOperation::Read); // Manually transition to AddressPhase (simulating chip select) ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); // Set address high nibble (0x0) on data bus set_data_bus(&ram, 0x0); // Generate Φ1 rising edge (no latching yet, data may not be stable) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - address latching happens here (data is now stable) generate_clock_edge(&mut ram, false, false); // Should have latched high nibble assert_eq!(ram.get_address_high_nibble(), Some(0x0)); println!( "After high nibble: state={:?}, high_nibble={:?}", ram.get_timing_state(), ram.get_address_high_nibble() ); // Set address low nibble (0x1) on data bus set_data_bus(&ram, 0x1); // Generate Φ1 rising edge (no latching yet) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - low nibble latching happens here generate_clock_edge(&mut ram, false, false); // Should have latched low nibble and transitioned to WaitLatency assert_eq!(ram.get_address_low_nibble(), Some(0x1)); println!( "After low nibble: high_nibble={:?}, low_nibble={:?}", ram.get_address_high_nibble(), ram.get_address_low_nibble() ); // Wait for latency to elapse (with 1ns access time, it should elapse immediately) std::thread::sleep(std::time::Duration::from_millis(1)); // Generate Φ2 rising edge to trigger latency check and data driving generate_clock_edge(&mut ram, false, true); // Should have transitioned to ReadData/DriveData state assert_eq!(ram.get_timing_state(), TimingState::DriveData); println!("After Φ2 rising: state={:?}", ram.get_timing_state()); // Clear data bus drivers to prepare for reading clear_data_bus_drivers(&ram); // Execute state logic to drive data ram.update(); // Check that RAM is driving data bus with correct data let d0_pin = ram.get_pin("D0").unwrap(); let d1_pin = ram.get_pin("D1").unwrap(); let d2_pin = ram.get_pin("D2").unwrap(); let d3_pin = ram.get_pin("D3").unwrap(); // Read data bus value let mut data_value = 0; if d0_pin.lock().unwrap().read() == PinValue::High { data_value |= 1 << 0; } if d1_pin.lock().unwrap().read() == PinValue::High { data_value |= 1 << 1; } if d2_pin.lock().unwrap().read() == PinValue::High { data_value |= 1 << 2; } if d3_pin.lock().unwrap().read() == PinValue::High { data_value |= 1 << 3; } // Should be reading value at address 0x01 (0x0B from our test data) assert_eq!(data_value, 0x0B); println!("Data bus value: 0x{:X} (expected: 0x0B)", data_value); // Generate Φ2 falling edge to complete operation generate_clock_edge(&mut ram, false, false); // Should have returned to idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("Final state: {:?}", ram.get_timing_state()); println!("✅ RAM read operation state progression test passed!"); } /// Test RAM write operation state progression /// /// Note: This test verifies the state machine behavior through direct state manipulation /// rather than through pin-based triggering. The Intel 4002 implementation processes /// events through process_event_direct() which doesn't call handle_component_event(), /// so chip select via pins doesn't work the same way as Intel 4001. /// /// Address latching happens on Φ1 falling edge (after data is stable). #[test] fn test_ram_write_state_progression() { println!("=== Testing RAM Write Operation State Progression ==="); let mut ram = create_test_ram(); // Initially should be in idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("Initial state: {:?}", ram.get_timing_state()); // Set operation type to Write ram.set_pending_operation(PendingOperation::Write); // Manually transition to AddressPhase (simulating chip select) ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); // Set address high nibble (0x0) on data bus set_data_bus(&ram, 0x0); // Generate Φ1 rising edge (no latching yet) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - address latching happens here generate_clock_edge(&mut ram, false, false); // Should have latched high nibble assert_eq!(ram.get_address_high_nibble(), Some(0x0)); println!( "After high nibble: state={:?}, high_nibble={:?}", ram.get_timing_state(), ram.get_address_high_nibble() ); // Set address low nibble (0x2) on data bus set_data_bus(&ram, 0x2); // Generate Φ1 rising edge (no latching yet) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - low nibble latching happens here generate_clock_edge(&mut ram, false, false); // Should have latched low nibble assert_eq!(ram.get_address_low_nibble(), Some(0x2)); println!( "After low nibble: high_nibble={:?}, low_nibble={:?}", ram.get_address_high_nibble(), ram.get_address_low_nibble() ); // Wait for latency to elapse (with 1ns access time, it should elapse immediately) std::thread::sleep(std::time::Duration::from_millis(1)); // Set data to write (0x0F) on data bus set_data_bus(&ram, 0x0F); // Generate Φ2 rising edge to handle data operation generate_clock_edge(&mut ram, false, true); // Should have transitioned to WriteData/DriveData state assert_eq!(ram.get_timing_state(), TimingState::DriveData); println!("After Φ2 rising: state={:?}", ram.get_timing_state()); // Generate Φ2 falling edge to complete operation generate_clock_edge(&mut ram, false, false); // Should have returned to idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("Final state: {:?}", ram.get_timing_state()); // Verify that data was written correctly assert_eq!(ram.read_ram(0x02).unwrap(), 0x0F); println!("Written data verified: address 0x02 = 0x0F"); println!("✅ RAM write operation state progression test passed!"); } /// Test output port operation state progression /// /// Note: This test verifies the state machine behavior through direct state manipulation /// rather than through pin-based triggering. The Intel 4002 implementation processes /// events through process_event_direct() which doesn't call handle_component_event(), /// so chip select via pins doesn't work the same way as Intel 4001. /// /// Address latching happens on Φ1 falling edge (after data is stable). #[test] fn test_output_port_state_progression() { println!("=== Testing Output Port Operation State Progression ==="); let mut ram = create_test_ram(); // Initially should be in idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("Initial state: {:?}", ram.get_timing_state()); // Set operation type to OutputPort ram.set_pending_operation(PendingOperation::OutputPort); // Manually transition to AddressPhase (simulating chip select) ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); // Set address high nibble (0x1) on data bus (for output port 0x14-0x17) set_data_bus(&ram, 0x1); // Generate Φ1 rising edge (no latching yet) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - address latching happens here generate_clock_edge(&mut ram, false, false); // Should have latched high nibble assert_eq!(ram.get_address_high_nibble(), Some(0x1)); println!( "After high nibble: state={:?}, high_nibble={:?}", ram.get_timing_state(), ram.get_address_high_nibble() ); // Set address low nibble (0x4) on data bus (for output port 0 address 0x14) set_data_bus(&ram, 0x4); // Generate Φ1 rising edge (no latching yet) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - low nibble latching happens here generate_clock_edge(&mut ram, false, false); // Should have latched low nibble assert_eq!(ram.get_address_low_nibble(), Some(0x4)); println!( "After low nibble: high_nibble={:?}, low_nibble={:?}", ram.get_address_high_nibble(), ram.get_address_low_nibble() ); // Wait for latency to elapse (with 1ns access time, it should elapse immediately) std::thread::sleep(std::time::Duration::from_millis(1)); // Set data to write (0x05) on data bus set_data_bus(&ram, 0x05); // Generate Φ2 rising edge to handle data operation generate_clock_edge(&mut ram, false, true); // Should have transitioned to OutputPort/DriveData state assert_eq!(ram.get_timing_state(), TimingState::DriveData); println!("After Φ2 rising: state={:?}", ram.get_timing_state()); // Generate Φ2 falling edge to complete operation generate_clock_edge(&mut ram, false, false); // Should have returned to idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("Final state: {:?}", ram.get_timing_state()); // Verify that output port was set correctly assert_eq!(ram.get_io_port(0).unwrap(), 0x05); println!("Output port 0 set to: 0x05"); println!("✅ Output port operation state progression test passed!"); } /// Test that unified state machine doesn't progress without proper signals #[test] fn test_no_progression_without_signals() { println!("=== Testing No State Progression Without Proper Signals ==="); let mut ram = create_test_ram(); // Initially should be in idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); // Generate clock cycles without proper control signals for i in 0..5 { generate_clock_cycle(&mut ram); println!("Cycle {}: state={:?}", i + 1, ram.get_timing_state()); // Should remain in idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); } println!("✅ No progression without signals test passed!"); } /// Test reset functionality /// /// Note: This test verifies the state machine behavior through direct state manipulation /// rather than through pin-based triggering. The Intel 4002 implementation processes /// events through process_event_direct() which doesn't call handle_component_event(), /// so chip select via pins doesn't work the same way as Intel 4001. /// /// Address latching happens on Φ1 falling edge (after data is stable). #[test] fn test_reset_functionality() { println!("=== Testing Reset Functionality ==="); let mut ram = create_test_ram(); // Manually transition to AddressPhase and set up some state ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); set_data_bus(&ram, 0x0); // Generate Φ1 rising edge (no latching yet) generate_clock_edge(&mut ram, true, false); // Generate Φ1 falling edge - address latching happens here generate_clock_edge(&mut ram, false, false); // Should have latched high nibble assert_eq!(ram.get_address_high_nibble(), Some(0x0)); println!( "Before reset: high_nibble={:?}", ram.get_address_high_nibble() ); // Assert RESET (active low) let reset_pin = ram.get_pin("RESET").unwrap(); { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } wait_for_pin_settlement(); // Update with RESET low - should clear all state ram.update(); // Should have returned to idle state assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("After reset: state={:?}", ram.get_timing_state()); // Verify address state is cleared assert_eq!(ram.get_address_high_nibble(), None); assert_eq!(ram.get_address_low_nibble(), None); assert_eq!(ram.get_full_address_ready(), false); println!("✅ Reset functionality test passed!"); } /// Test complete unified state machine progression for all operations /// /// Note: This test verifies the state machine behavior through direct state manipulation /// rather than through pin-based triggering. The Intel 4002 implementation processes /// events through process_event_direct() which doesn't call handle_component_event(), /// so chip select via pins doesn't work the same way as Intel 4001. /// /// Address latching happens on Φ1 falling edge (after data is stable). #[test] fn test_complete_state_machine_progression() { println!("=== Testing Complete Unified State Machine Progression ==="); let mut ram = create_test_ram(); // Test 1: RAM Read Operation println!("Test 1: RAM Read Operation"); ram.set_pending_operation(PendingOperation::Read); ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); set_data_bus(&ram, 0x0); generate_clock_edge(&mut ram, true, false); // Φ1 rising (no latching yet) generate_clock_edge(&mut ram, false, false); // Φ1 falling - latch high nibble set_data_bus(&ram, 0x1); generate_clock_edge(&mut ram, true, false); // Φ1 rising (no latching yet) generate_clock_edge(&mut ram, false, false); // Φ1 falling - latch low nibble clear_data_bus_drivers(&ram); std::thread::sleep(std::time::Duration::from_millis(1)); // Wait for latency generate_clock_edge(&mut ram, false, true); // Φ2 rising - read data generate_clock_edge(&mut ram, false, false); // Φ2 falling - return to idle assert_eq!(ram.get_timing_state(), TimingState::Idle); println!("✅ RAM Read Operation completed"); // Test 2: RAM Write Operation println!("Test 2: RAM Write Operation"); ram.set_pending_operation(PendingOperation::Write); ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); println!( "Test 2: After set_state_for_test - nibbles: high={:?}, low={:?}", ram.get_address_high_nibble(), ram.get_address_low_nibble() ); set_data_bus(&ram, 0x0); generate_clock_edge(&mut ram, true, false); // Φ1 rising (no latching yet) generate_clock_edge(&mut ram, false, false); // Φ1 falling - latch high nibble println!( "Test 2: After first Φ1 falling - nibbles: high={:?}, low={:?}", ram.get_address_high_nibble(), ram.get_address_low_nibble() ); set_data_bus(&ram, 0x2); generate_clock_edge(&mut ram, true, false); // Φ1 rising (no latching yet) generate_clock_edge(&mut ram, false, false); // Φ1 falling - latch low nibble println!( "Test 2: After second Φ1 falling - nibbles: high={:?}, low={:?}, full_address_ready={}", ram.get_address_high_nibble(), ram.get_address_low_nibble(), ram.get_full_address_ready() ); set_data_bus(&ram, 0x0F); println!("Test 2: Data bus set to 0x0F"); std::thread::sleep(std::time::Duration::from_millis(1)); // Wait for latency println!( "Test 2: Before Φ2 rising - state={:?}", ram.get_timing_state() ); generate_clock_edge(&mut ram, false, true); // Φ2 rising - write data println!( "Test 2: After Φ2 rising - state={:?}", ram.get_timing_state() ); generate_clock_edge(&mut ram, false, false); // Φ2 falling - return to idle println!( "Test 2: After Φ2 falling - state={:?}", ram.get_timing_state() ); println!( "Test 2: RAM[0x02]={:02X}, expected=0x0F", ram.read_ram(0x02).unwrap() ); assert_eq!(ram.get_timing_state(), TimingState::Idle); assert_eq!(ram.read_ram(0x02).unwrap(), 0x0F); println!("✅ RAM Write Operation completed"); // Test 3: Output Port Operation println!("Test 3: Output Port Operation"); ram.set_pending_operation(PendingOperation::OutputPort); ram.set_state_for_test(Intel400xUnifiedState::AddressPhase); set_data_bus(&ram, 0x1); generate_clock_edge(&mut ram, true, false); // Φ1 rising (no latching yet) generate_clock_edge(&mut ram, false, false); // Φ1 falling - latch high nibble set_data_bus(&ram, 0x4); generate_clock_edge(&mut ram, true, false); // Φ1 rising (no latching yet) generate_clock_edge(&mut ram, false, false); // Φ1 falling - latch low nibble set_data_bus(&ram, 0x05); std::thread::sleep(std::time::Duration::from_millis(1)); // Wait for latency generate_clock_edge(&mut ram, false, true); // Φ2 rising - write to output port generate_clock_edge(&mut ram, false, false); // Φ2 falling - return to idle assert_eq!(ram.get_timing_state(), TimingState::Idle); assert_eq!(ram.get_io_port(0).unwrap(), 0x05); println!("✅ Output Port Operation completed"); println!("✅ Complete unified state machine progression test passed!"); }