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 latches low nibble on PHI1 rising edge // (address nibbles are latched on Φ1 RISING edge, same as CPU drives address) { 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 latched low nibble and transitioned to WaitLatency // (both nibbles latched on successive PHI1 rising edges) let state_after_low_nibble = rom.get_timing_state(); assert!( state_after_low_nibble == TimingState::WaitLatency, "Expected WaitLatency after latching both nibbles on PHI1 rising, got {:?}", state_after_low_nibble ); println!( "State after second Φ1 rising (both nibbles 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"); } // ROM is in WaitLatency with a3_wait_cycles=1. // Need to cycle the clock to get through the wait period and into DriveData. // The ROM expects PHI2 rising edges to decrement a3_wait_cycles and transition. // Step 1: PHI1 falling + PHI2 rising (decrements a3_wait_cycles to 0) { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // a3_wait_cycles decremented from 1 to 0 // Step 2: PHI2 falling + PHI1 rising (no transition yet, just clock edge) { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Step 3: PHI1 falling + PHI2 rising (a3_wait_cycles=0, transitions to DriveData) { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Should now be in DriveData (process_latency_elapsed on this PHI2 rising) // But return statement means data isn't driven yet // Step 4: PHI2 falling + PHI1 rising { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Step 5: PHI1 falling + PHI2 rising - NOW ROM drives data { let mut phi1_guard = phi1_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } { let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Should be in DriveData and driving OPR (high nibble of 0xAB = 0xA) assert_eq!(rom.get_timing_state(), TimingState::DriveData); println!("State after PHI2 rising (driving data): {:?}", rom.get_timing_state()); // Wait for pins to settle after ROM drives data thread::sleep(Duration::from_millis(1)); // Verify OPR is being driven on the bus // Data 0xAB has high nibble 0xA = 1010 (D0=0, D1=1, D2=0, D3=1) assert_eq!(d0_pin.lock().read(), PinValue::Low, "D0 should be Low for 0xA"); assert_eq!(d1_pin.lock().read(), PinValue::High, "D1 should be High for 0xA"); assert_eq!(d2_pin.lock().read(), PinValue::Low, "D2 should be Low for 0xA"); assert_eq!(d3_pin.lock().read(), PinValue::High, "D3 should be High for 0xA"); println!("OPR data verified: 0xA"); } /// 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); }