Added more tests
Extracted common 400x behavior like timing and latching into common module Added maxed MCS-4
This commit is contained in:
parent
dd93e33087
commit
c039e9069a
25 changed files with 5694 additions and 494 deletions
369
tests/intel_400x_tests.rs
Normal file
369
tests/intel_400x_tests.rs
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
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<String, Arc<Mutex<TestPin>>>,
|
||||
}
|
||||
|
||||
#[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<String>, 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<Mutex<Pin>>) {
|
||||
self.pins.insert(name.to_string(), pin);
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<TestPin>>, 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<String, Arc<Mutex<rusty_emu::pin::Pin>>> {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<rusty_emu::pin::Pin>>, 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<u8> = None;
|
||||
let mut low_nibble: Option<u8> = None;
|
||||
let mut full_address_ready = false;
|
||||
let mut address_latch_time: Option<Instant> = 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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue