use std::thread; use std::time::Duration; use rusty_emu::component::Component; use rusty_emu::components::common::intel_400x::{Intel400xTimingState, TimingState}; use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::pin::PinValue; /// Test to verify that Intel 4001 ROM unified state machine progresses correctly /// This test isolates unified state machine logic to verify proper state transitions #[test] fn test_intel_4001_state_machine_progression() { let mut rom = Intel4001::new_with_access_time("StateMachineTestROM".to_string(), 1); // 1ns for fast testing // Load test data: address 0x12 should contain 0xAB let test_data = vec![0x00; 0x12]; // Initialize with zeros let mut data_with_test = test_data; data_with_test.push(0xAB); // Add 0xAB at address 0x12 rom.load_rom_data(data_with_test, 0).unwrap(); // Get pin references for simulation 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 phi1_pin = rom.get_pin("PHI1").unwrap(); let phi2_pin = rom.get_pin("PHI2").unwrap(); let reset_pin = rom.get_pin("RESET").unwrap(); // Data pins for address and data verification 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(); // Start component rom.base.set_running(true); assert!(rom.is_running()); // Initialize clock pins to Low { let mut phi1_guard = phi1_pin.lock(); let mut phi2_guard = phi2_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Initialize control pins // Note: RESET is active LOW, so set it to High to ensure component is not in reset { let mut sync_guard = sync_pin.lock(); let mut cm_guard = cm_pin.lock(); let mut ci_guard = ci_pin.lock(); let mut reset_guard = reset_pin.lock(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low); cm_guard.set_driver(Some("TEST".to_string()), PinValue::Low); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Not in reset } // Initialize data pins to Low { let mut d0_guard = d0_pin.lock(); let mut d1_guard = d1_pin.lock(); let mut d2_guard = d2_pin.lock(); let mut d3_guard = d3_pin.lock(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to initialize clock states rom.update(); // Verify initial state assert_eq!(rom.get_timing_state(), TimingState::Idle); println!("Initial state: {:?}", rom.get_timing_state()); // Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access) { 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); // Memory access } // Set up address high nibble (0x1 for address 0x12) { let mut d0_guard = d0_pin.lock(); let mut d1_guard = d1_pin.lock(); let mut d2_guard = d2_pin.lock(); let mut d3_guard = d3_pin.lock(); // 0x1 = 0001 (D0=1, D1=0, D2=0, D3=0) d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Wait for pins to settle thread::sleep(Duration::from_millis(1)); // Trigger Φ1 rising edge to start memory operation and latch high nibble { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Update to process Φ1 rising edge rom.update(); // Should have started memory operation and transitioned to AddressPhase assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); println!( "State after Φ1 rising (high nibble): {:?}", rom.get_timing_state() ); // Reset Φ1 to Low { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to process Φ1 falling edge rom.update(); // Set up address low nibble (0x2 for address 0x12) { let mut d0_guard = d0_pin.lock(); let mut d1_guard = d1_pin.lock(); let mut d2_guard = d2_pin.lock(); let mut d3_guard = d3_pin.lock(); // 0x2 = 0010 (D0=0, D1=1, D2=0, D3=0) d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d1_guard.set_driver(Some("TEST".to_string()), PinValue::High); d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Wait for pins to settle thread::sleep(Duration::from_millis(1)); // Trigger Φ1 rising edge again - ROM should stay in AddressPhase // (address nibbles are latched on Φ1 falling edge, not rising) { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Update to process Φ1 rising edge rom.update(); // Should still be in AddressPhase (latching happens on falling edge) assert_eq!( rom.get_timing_state(), TimingState::AddressPhase, "Should still be in AddressPhase after Φ1 rising - latching happens on falling edge" ); println!( "State after Φ1 rising (before falling): {:?}", rom.get_timing_state() ); // Reset Φ1 to Low to trigger falling edge - this is when low nibble is latched { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to process Φ1 falling edge - low nibble is latched here rom.update(); // Should have latched low nibble and transitioned to WaitLatency or DriveData // With 1ns access time, it might transition directly to DriveData let state_after_low_nibble = rom.get_timing_state(); assert!( state_after_low_nibble == TimingState::WaitLatency || state_after_low_nibble == TimingState::DriveData, "Expected WaitLatency or DriveData after latching both nibbles, got {:?}", state_after_low_nibble ); println!( "State after Φ1 falling (low nibble latched): {:?}", state_after_low_nibble ); // Remove test drivers from data pins to allow ROM to drive bus { let mut d0_guard = d0_pin.lock(); let mut d1_guard = d1_pin.lock(); let mut d2_guard = d2_pin.lock(); let mut d3_guard = d3_pin.lock(); d0_guard.remove_driver("TEST"); d1_guard.remove_driver("TEST"); d2_guard.remove_driver("TEST"); d3_guard.remove_driver("TEST"); } // If still in WaitLatency, trigger Φ2 rising edge to check latency and drive data if rom.get_timing_state() == TimingState::WaitLatency { // Trigger Φ2 rising edge { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Update to process Φ2 rising edge rom.update(); // Should have transitioned to DriveData assert_eq!(rom.get_timing_state(), TimingState::DriveData); println!("State after Φ2 rising: {:?}", rom.get_timing_state()); } // Wait for pins to settle after ROM drives data thread::sleep(Duration::from_millis(1)); // Verify data is being driven on the bus // Data 0xAB has low nibble 0xB = 1011 (D0=1, D1=1, D2=0, D3=1) // Note: Due to bit ordering, D0 should be High, D1 should be High, D2 should be Low, D3 should be High assert_eq!(d0_pin.lock().read(), PinValue::High); assert_eq!(d1_pin.lock().read(), PinValue::High); assert_eq!(d2_pin.lock().read(), PinValue::Low); assert_eq!(d3_pin.lock().read(), PinValue::High); // Reset Φ2 to Low { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to process Φ2 falling edge rom.update(); // Should have returned to Idle assert_eq!(rom.get_timing_state(), TimingState::Idle); println!("Final state: {:?}", rom.get_timing_state()); // Verify data bus is tri-stated 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 to verify that unified state machine doesn't progress without proper control signals #[test] fn test_intel_4001_state_machine_no_progression_without_signals() { let mut rom = Intel4001::new_with_access_time("NoProgressionTestROM".to_string(), 1); // Get pin references for simulation let phi1_pin = rom.get_pin("PHI1").unwrap(); let phi2_pin = rom.get_pin("PHI2").unwrap(); // Start component rom.base.set_running(true); assert!(rom.is_running()); // Initialize clock pins to Low { let mut phi1_guard = phi1_pin.lock(); let mut phi2_guard = phi2_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to initialize clock states rom.update(); // Verify initial state assert_eq!(rom.get_timing_state(), TimingState::Idle); // Trigger clock edges without setting up control signals { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Update to process Φ1 rising edge rom.update(); // Should still be in Idle state since control signals are not set assert_eq!(rom.get_timing_state(), TimingState::Idle); // Reset Φ1 to Low { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to process Φ1 falling edge rom.update(); // Trigger Φ2 rising edge { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Update to process Φ2 rising edge rom.update(); // Should still be in Idle state assert_eq!(rom.get_timing_state(), TimingState::Idle); } /// Test to verify that reset properly clears unified state machine #[test] fn test_intel_4001_state_machine_reset() { let mut rom = Intel4001::new_with_access_time("ResetTestROM".to_string(), 1); // Get pin references for simulation 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 phi1_pin = rom.get_pin("PHI1").unwrap(); let reset_pin = rom.get_pin("RESET").unwrap(); // Start component rom.base.set_running(true); assert!(rom.is_running()); // Initialize clock pins to Low { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to initialize clock states rom.update(); // Verify initial state assert_eq!(rom.get_timing_state(), TimingState::Idle); // Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access) { 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); // Memory access } // Trigger Φ1 rising edge to start memory operation { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Update to process Φ1 rising edge rom.update(); // Should have started memory operation and transitioned to AddressPhase assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // Assert RESET (active LOW - set to Low to trigger reset) { let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Update to process reset rom.update(); // Should have returned to Idle state assert_eq!(rom.get_timing_state(), TimingState::Idle); }