Created factory for system creation.

Systems are now defined in JSON rather than hard-coded
This commit is contained in:
Jan-Peter von Hunnius 2025-09-29 12:41:16 +02:00
parent c039e9069a
commit 6f3295dc5e
31 changed files with 1610 additions and 1709 deletions

View file

@ -5,16 +5,9 @@ edition = "2021"
[dependencies] [dependencies]
tempfile = "3.3.0" tempfile = "3.3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dev-dependencies] [dev-dependencies]
# Testing framework
tokio-test = "0.4"
# Mock testing
mockall = "0.12"
# Property-based testing # Property-based testing
proptest = "1.0" proptest = "1.0"
# Additional testing utilities
pretty_assertions = "1.3"

View file

@ -1,10 +1,12 @@
# Rusty Emulator # Rusty Emulator
A comprehensive Intel 4004/4001 microprocessor simulator written in Rust, featuring cycle-accurate emulation and extensive testing capabilities. A comprehensive Intel 4004/4001 microprocessor simulator written in Rust, featuring cycle-accurate emulation and
extensive testing capabilities.
## Overview ## Overview
Rusty Emulator is a detailed simulation of Intel's first microprocessor system, the MCS-4 (Micro Computer System). It provides: Rusty Emulator is a detailed simulation of Intel's first microprocessor system, the MCS-4 (Micro Computer System). It
provides:
- **Hardware-Accurate Simulation**: Cycle-accurate timing where possible - **Hardware-Accurate Simulation**: Cycle-accurate timing where possible
- **Comprehensive Testing**: Extensive test suite with multiple testing strategies - **Comprehensive Testing**: Extensive test suite with multiple testing strategies
@ -127,7 +129,8 @@ system.run();
## Testing ## Testing
The project includes a comprehensive test suite demonstrating that the `intel_400x` common functionality is highly testable: The project includes a comprehensive test suite demonstrating that the `intel_400x` common functionality is highly
testable:
### Test Categories ### Test Categories
@ -154,6 +157,7 @@ cargo test -- --nocapture
### Test Results ### Test Results
The test suite demonstrates: The test suite demonstrates:
- ✅ **Main library compiles successfully** - ✅ **Main library compiles successfully**
- ✅ **Core functionality works correctly** - ✅ **Core functionality works correctly**
- ✅ **State management operates properly** - ✅ **State management operates properly**
@ -176,7 +180,9 @@ The test suite demonstrates:
## Key Technical Achievements ## Key Technical Achievements
### 1. Testability Demonstration ### 1. Testability Demonstration
**Answer to Original Question: CONFIRMED** **Answer to Original Question: CONFIRMED**
- ✅ **Yes, it is absolutely possible to write meaningful test cases for the generic intel_400x file** - ✅ **Yes, it is absolutely possible to write meaningful test cases for the generic intel_400x file**
- ✅ **Pure functions**: Address assembly, clock logic, state queries are easily testable - ✅ **Pure functions**: Address assembly, clock logic, state queries are easily testable
- ✅ **Mockable architecture**: Trait-based design enables comprehensive mocking - ✅ **Mockable architecture**: Trait-based design enables comprehensive mocking
@ -184,12 +190,14 @@ The test suite demonstrates:
- ✅ **Integration testing**: Real components work correctly with common traits - ✅ **Integration testing**: Real components work correctly with common traits
### 2. Comprehensive Test Suite ### 2. Comprehensive Test Suite
- **416 lines** of comprehensive test code - **416 lines** of comprehensive test code
- **Multiple testing strategies**: Unit, mock-based, property-based, integration - **Multiple testing strategies**: Unit, mock-based, property-based, integration
- **High test coverage**: Core functionality, edge cases, error conditions - **High test coverage**: Core functionality, edge cases, error conditions
- **Working examples**: Demonstrates practical usage patterns - **Working examples**: Demonstrates practical usage patterns
### 3. Clean Architecture ### 3. Clean Architecture
- **Trait-based design**: Enables code reuse and testing - **Trait-based design**: Enables code reuse and testing
- **Separation of concerns**: Clear boundaries between components - **Separation of concerns**: Clear boundaries between components
- **Extensible structure**: Easy addition of new components - **Extensible structure**: Easy addition of new components
@ -198,6 +206,7 @@ The test suite demonstrates:
## Development Status ## Development Status
### ✅ Completed ### ✅ Completed
- Core component architecture - Core component architecture
- Intel 4001 ROM implementation - Intel 4001 ROM implementation
- Intel 4004 CPU structure - Intel 4004 CPU structure
@ -206,12 +215,14 @@ The test suite demonstrates:
- Documentation system - Documentation system
### 🚧 In Progress ### 🚧 In Progress
- Complete Intel 4004 instruction execution - Complete Intel 4004 instruction execution
- Intel 4002 RAM implementation - Intel 4002 RAM implementation
- System integration and testing - System integration and testing
- Performance optimization - Performance optimization
### 📋 Planned ### 📋 Planned
- Additional CPU architectures (6502, 65C02) - Additional CPU architectures (6502, 65C02)
- Enhanced I/O device support - Enhanced I/O device support
- Development tools integration - Development tools integration
@ -245,7 +256,8 @@ This project demonstrates:
## License ## License
This project is educational and demonstrates microprocessor simulation techniques. See individual source files for specific licensing information. This project is educational and demonstrates microprocessor simulation techniques. See individual source files for
specific licensing information.
## Acknowledgments ## Acknowledgments
@ -255,4 +267,5 @@ This project is educational and demonstrates microprocessor simulation technique
--- ---
**Note**: This is an educational simulation of historical hardware. While it aims for accuracy, some aspects are simplified for educational purposes and modern Rust implementation requirements. **Note**: This is an educational simulation of historical hardware. While it aims for accuracy, some aspects are
simplified for educational purposes and modern Rust implementation requirements.

110
configs/mcs4_basic.json Normal file
View file

@ -0,0 +1,110 @@
{
"name": "IntelMcs4",
"description": "Basic MCS-4 System with CPU, Clock, 2 ROMs, and 1 RAM",
"version": "1.0",
"metadata": {
"author": "RustyEmu",
"created": "2024-01-01",
"cpu_speed": 750000,
"architecture": "mcs4",
"rom_count": 2,
"ram_count": 1
},
"components": {
"CPU_4004": {
"single": {
"component_type": "intel_4004",
"name": "CPU_4004",
"properties": {
"clock_speed": 750000
}
}
},
"SYSTEM_CLOCK": {
"single": {
"component_type": "generic_clock",
"name": "SYSTEM_CLOCK",
"properties": {
"frequency": 750000
}
}
},
"ROM_4001_1": {
"single": {
"component_type": "intel_4001",
"name": "ROM_4001_1",
"properties": {}
}
},
"ROM_4001_2": {
"single": {
"component_type": "intel_4001",
"name": "ROM_4001_2",
"properties": {}
}
},
"RAM_4002": {
"single": {
"component_type": "intel_4002",
"name": "RAM_4002",
"properties": {
"variant": "Type1",
"access_time": 500
}
}
}
},
"connections": {
"clock_signals": {
"connection_type": "clock",
"source": {
"component": "SYSTEM_CLOCK",
"pin": "CLK"
},
"targets": [
{"component": "CPU_4004", "pin": "PHI1"},
{"component": "CPU_4004", "pin": "PHI2"},
{"component": "ROM_4001_1", "pin": "PHI1"},
{"component": "ROM_4001_1", "pin": "PHI2"},
{"component": "ROM_4001_2", "pin": "PHI1"},
{"component": "ROM_4001_2", "pin": "PHI2"},
{"component": "RAM_4002", "pin": "PHI1"},
{"component": "RAM_4002", "pin": "PHI2"}
]
},
"data_bus": {
"connection_type": "data_bus",
"source": {
"component": "CPU_4004",
"pin": "D0"
},
"targets": [
{"component": "ROM_4001_1", "pin": "D0"},
{"component": "ROM_4001_2", "pin": "D0"},
{"component": "RAM_4002", "pin": "D0"}
]
},
"control_signals": {
"connection_type": "control",
"source": {
"component": "CPU_4004",
"pin": "SYNC"
},
"targets": [
{"component": "ROM_4001_1", "pin": "SYNC"},
{"component": "ROM_4001_2", "pin": "SYNC"},
{"component": "RAM_4002", "pin": "SYNC"}
]
}
},
"layout": {
"grid_size": [10, 10],
"positions": {
"cpu": [1, 1],
"clock": [0, 0],
"rom1": [2, 0],
"rom2": [3, 0],
"ram": [2, 2]
}
}
}

167
configs/mcs4_max.json Normal file
View file

@ -0,0 +1,167 @@
{
"name": "IntelMcs4Max",
"description": "Fig.1 MCS-4 System with 16 ROMs and 16 RAMs",
"version": "1.0",
"metadata": {
"configuration": "fig1",
"rom_count": 16,
"ram_count": 16,
"shift_register_count": 3,
"cpu_speed": 750000,
"architecture": "mcs4_max"
},
"components": {
"cpu": {
"single": {
"component_type": "intel_4004",
"name": "CPU_4004",
"properties": {
"clock_speed": 750000
}
}
},
"clock": {
"single": {
"component_type": "generic_clock",
"name": "SYSTEM_CLOCK",
"properties": {
"frequency": 750000
}
}
},
"roms": {
"array": {
"component_type": "intel_4001",
"count": 16,
"naming_pattern": "ROM_4001_{:02}",
"properties": {}
}
},
"rams": {
"array": {
"component_type": "intel_4002",
"count": 16,
"naming_pattern": "RAM_4002_{:02}",
"properties": {
"variant": "Type1",
"access_time": 500
},
"overrides": {
"03": {
"variant": "Type2"
}
}
}
},
"shift_registers": {
"array": {
"component_type": "intel_4003",
"count": 3,
"names": [
"SHIFT_4003_ROM15",
"SHIFT_4003_RAM3_1",
"SHIFT_4003_RAM3_2"
],
"properties": {}
}
}
},
"connections": {
"clock_signals": {
"connection_type": "clock",
"source": {
"component": "clock",
"pin": "CLK"
},
"targets": [
{"component": "cpu", "pin": "PHI1"},
{"component": "cpu", "pin": "PHI2"}
]
},
"rom_clock_signals": {
"connection_type": "clock_fanout",
"source": {
"component": "clock",
"pin": "CLK"
},
"target_pattern": {
"component_pattern": "ROM_4001_{:02}",
"pins": ["PHI1", "PHI2"]
}
},
"ram_clock_signals": {
"connection_type": "clock_fanout",
"source": {
"component": "clock",
"pin": "CLK"
},
"target_pattern": {
"component_pattern": "RAM_4002_{:02}",
"pins": ["PHI1", "PHI2"]
}
},
"shift_clock_signals": {
"connection_type": "clock_fanout",
"source": {
"component": "clock",
"pin": "CLK"
},
"target_pattern": {
"component_pattern": "SHIFT_4003_ROM15",
"pins": ["PHI1", "PHI2"]
}
},
"shift_ram3_clock_signals": {
"connection_type": "clock_fanout",
"source": {
"component": "clock",
"pin": "CLK"
},
"targets": [
{"component": "SHIFT_4003_RAM3_1", "pin": "PHI1"},
{"component": "SHIFT_4003_RAM3_1", "pin": "PHI2"},
{"component": "SHIFT_4003_RAM3_2", "pin": "PHI1"},
{"component": "SHIFT_4003_RAM3_2", "pin": "PHI2"}
]
},
"rom15_shift_register": {
"connection_type": "io_chain",
"source_component": "ROM_4001_15",
"source_pins": ["IO0", "IO1", "IO2", "IO3"],
"target_component": "SHIFT_4003_ROM15",
"target_pins": ["D0", "D1", "D2", "D3"]
},
"ram3_shift_chain": {
"connection_type": "serial_chain",
"stages": [
{
"source_component": "RAM_4002_03",
"source_pins": ["O0", "O1", "O2", "O3"],
"target_component": "SHIFT_4003_RAM3_1",
"target_pins": ["D0", "D1", "D2", "D3"]
},
{
"source_component": "SHIFT_4003_RAM3_1",
"source_pins": ["O0", "O1", "O2", "O3"],
"target_component": "SHIFT_4003_RAM3_2",
"target_pins": ["D0", "D1", "D2", "D3"]
}
]
}
},
"layout": {
"grid_size": [20, 20],
"positions": {
"cpu": [1, 1],
"clock": [0, 0],
"ROM_4001_00": [2, 0],
"ROM_4001_15": [17, 0],
"RAM_4002_00": [2, 2],
"RAM_4002_03": [5, 2],
"RAM_4002_15": [17, 2],
"SHIFT_4003_ROM15": [18, 0],
"SHIFT_4003_RAM3_1": [6, 2],
"SHIFT_4003_RAM3_2": [7, 2]
}
}
}

View file

@ -1,6 +1,7 @@
# Source Code Architecture # Source Code Architecture
This directory contains the core implementation of the Rusty Emulator, an Intel 4004/4001 microprocessor simulator written in Rust. This directory contains the core implementation of the Rusty Emulator, an Intel 4004/4001 microprocessor simulator
written in Rust.
## Architecture Overview ## Architecture Overview
@ -29,7 +30,9 @@ src/
## Core Abstractions ## Core Abstractions
### Component System ### Component System
The emulator is built around a component-based architecture where each hardware element implements the `Component` trait:
The emulator is built around a component-based architecture where each hardware element implements the `Component`
trait:
```rust ```rust
pub trait Component: Send + Sync { pub trait Component: Send + Sync {
@ -44,22 +47,27 @@ pub trait Component: Send + Sync {
``` ```
### Pin System ### Pin System
All components communicate through pins that can be in one of three states: All components communicate through pins that can be in one of three states:
- `High` (logic 1) - `High` (logic 1)
- `Low` (logic 0) - `Low` (logic 0)
- `HighZ` (high impedance/tri-state) - `HighZ` (high impedance/tri-state)
### Clocking ### Clocking
The system uses a two-phase clock architecture (Φ1, Φ2) that matches the Intel 4004's requirements. The system uses a two-phase clock architecture (Φ1, Φ2) that matches the Intel 4004's requirements.
## Component Categories ## Component Categories
### CPU Components (`src/components/cpu/`) ### CPU Components (`src/components/cpu/`)
- **Intel4004**: Complete Intel 4004 CPU implementation - **Intel4004**: Complete Intel 4004 CPU implementation
- **MOS6502**: MOS Technology 6502 CPU (placeholder) - **MOS6502**: MOS Technology 6502 CPU (placeholder)
- **WDC65C02**: Western Design Center 65C02 CPU (placeholder) - **WDC65C02**: Western Design Center 65C02 CPU (placeholder)
### Memory Components (`src/components/memory/`) ### Memory Components (`src/components/memory/`)
- **Intel4001**: 256-byte ROM with 4-bit I/O ports - **Intel4001**: 256-byte ROM with 4-bit I/O ports
- **Intel4002**: 320-bit RAM with 4-bit output ports - **Intel4002**: 320-bit RAM with 4-bit output ports
- **Intel4003**: 10-bit shift register - **Intel4003**: 10-bit shift register
@ -67,6 +75,7 @@ The system uses a two-phase clock architecture (Φ1, Φ2) that matches the Intel
- **GenericROM**: Generic ROM implementation - **GenericROM**: Generic ROM implementation
### Common Functionality (`src/components/common/`) ### Common Functionality (`src/components/common/`)
- **Intel400x**: Shared traits and utilities for all Intel 400x series chips - **Intel400x**: Shared traits and utilities for all Intel 400x series chips
- Clock handling and edge detection - Clock handling and edge detection
- Data bus operations - Data bus operations
@ -76,12 +85,15 @@ The system uses a two-phase clock architecture (Φ1, Φ2) that matches the Intel
- Timing state machines - Timing state machines
### Clock Components (`src/components/clock/`) ### Clock Components (`src/components/clock/`)
- **GenericClock**: Configurable clock generator - **GenericClock**: Configurable clock generator
## System Integration (`src/systems/`) ## System Integration (`src/systems/`)
### Intel MCS-4 System ### Intel MCS-4 System
Complete Intel MCS-4 (Micro Computer System) implementation featuring: Complete Intel MCS-4 (Micro Computer System) implementation featuring:
- Intel 4004 CPU - Intel 4004 CPU
- Intel 4001 ROM units - Intel 4001 ROM units
- Intel 4002 RAM units - Intel 4002 RAM units
@ -92,18 +104,21 @@ Complete Intel MCS-4 (Micro Computer System) implementation featuring:
## Key Features ## Key Features
### Hardware-Accurate Simulation ### Hardware-Accurate Simulation
- Cycle-accurate timing where possible - Cycle-accurate timing where possible
- Two-phase clock operation - Two-phase clock operation
- Proper pin drive and tri-state handling - Proper pin drive and tri-state handling
- Bus contention detection and avoidance - Bus contention detection and avoidance
### Extensible Architecture ### Extensible Architecture
- Trait-based component system - Trait-based component system
- Easy addition of new CPU types - Easy addition of new CPU types
- Pluggable memory implementations - Pluggable memory implementations
- Configurable system topologies - Configurable system topologies
### Testing Support ### Testing Support
- Comprehensive test suite - Comprehensive test suite
- Mock components for isolated testing - Mock components for isolated testing
- Property-based testing for verification - Property-based testing for verification
@ -112,6 +127,7 @@ Complete Intel MCS-4 (Micro Computer System) implementation featuring:
## Usage Examples ## Usage Examples
### Basic Component Usage ### Basic Component Usage
```rust ```rust
use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::components::memory::intel_4001::Intel4001;
@ -127,6 +143,7 @@ rom.update(); // Process one clock cycle
``` ```
### System Integration ### System Integration
```rust ```rust
use rusty_emu::systems::intel_mcs_4::IntelMCS4; use rusty_emu::systems::intel_mcs_4::IntelMCS4;
@ -143,6 +160,7 @@ system.run();
## Development Guidelines ## Development Guidelines
### Adding New Components ### Adding New Components
1. Implement the `Component` trait 1. Implement the `Component` trait
2. Add appropriate pin configuration 2. Add appropriate pin configuration
3. Implement timing-accurate behavior 3. Implement timing-accurate behavior
@ -150,12 +168,14 @@ system.run();
5. Update module exports 5. Update module exports
### Testing Requirements ### Testing Requirements
- Unit tests for individual components - Unit tests for individual components
- Integration tests for component interactions - Integration tests for component interactions
- Property-based tests for behavioral verification - Property-based tests for behavioral verification
- Mock-based tests for external dependencies - Mock-based tests for external dependencies
### Code Organization ### Code Organization
- Keep components focused on single responsibilities - Keep components focused on single responsibilities
- Use traits for shared behavior - Use traits for shared behavior
- Maintain clear separation between hardware abstraction and implementation - Maintain clear separation between hardware abstraction and implementation

View file

@ -402,8 +402,7 @@ mod tests {
// assert!(bus.is_running()); // assert!(bus.is_running());
let pattern = vec![PinValue::High, PinValue::Low, PinValue::High, PinValue::Low]; let pattern = vec![PinValue::High, PinValue::Low, PinValue::High, PinValue::Low];
bus.set_test_pattern(pattern);
// bus.set_test_pattern(pattern);
// bus.set_pattern_interval(Duration::from_millis(10)); // bus.set_pattern_interval(Duration::from_millis(10));
// Give the thread a moment to start // Give the thread a moment to start

View file

@ -1,6 +1,7 @@
# Hardware Components # Hardware Components
This directory contains implementations of various hardware components used in the Intel 4004 microprocessor system and other architectures. This directory contains implementations of various hardware components used in the Intel 4004 microprocessor system and
other architectures.
## Component Architecture ## Component Architecture
@ -16,6 +17,7 @@ All components implement the base `Component` trait defined in `src/component.rs
### CPU Components (`cpu/`) ### CPU Components (`cpu/`)
#### Intel 4004 CPU #### Intel 4004 CPU
- **File**: `intel_4004.rs` - **File**: `intel_4004.rs`
- **Features**: - **Features**:
- Complete Intel 4004 instruction set implementation - Complete Intel 4004 instruction set implementation
@ -27,11 +29,13 @@ All components implement the base `Component` trait defined in `src/component.rs
- **Status**: Fully implemented with comprehensive testing - **Status**: Fully implemented with comprehensive testing
#### MOS 6502 CPU (Placeholder) #### MOS 6502 CPU (Placeholder)
- **File**: `mos_6502.rs` - **File**: `mos_6502.rs`
- **Features**: Placeholder for MOS Technology 6502 implementation - **Features**: Placeholder for MOS Technology 6502 implementation
- **Status**: Not yet implemented - **Status**: Not yet implemented
#### WDC 65C02 CPU (Placeholder) #### WDC 65C02 CPU (Placeholder)
- **File**: `wdc_65c02.rs` - **File**: `wdc_65c02.rs`
- **Features**: Placeholder for Western Design Center 65C02 implementation - **Features**: Placeholder for Western Design Center 65C02 implementation
- **Status**: Not yet implemented - **Status**: Not yet implemented
@ -39,6 +43,7 @@ All components implement the base `Component` trait defined in `src/component.rs
### Memory Components (`memory/`) ### Memory Components (`memory/`)
#### Intel 4001 ROM #### Intel 4001 ROM
- **File**: `intel_4001.rs` - **File**: `intel_4001.rs`
- **Features**: - **Features**:
- 256 bytes of mask-programmable ROM - 256 bytes of mask-programmable ROM
@ -49,6 +54,7 @@ All components implement the base `Component` trait defined in `src/component.rs
- **Status**: Fully implemented with comprehensive testing - **Status**: Fully implemented with comprehensive testing
#### Intel 4002 RAM #### Intel 4002 RAM
- **File**: `intel_4002.rs` - **File**: `intel_4002.rs`
- **Features**: - **Features**:
- 320 bits of read/write memory (80 × 4-bit characters) - 320 bits of read/write memory (80 × 4-bit characters)
@ -59,6 +65,7 @@ All components implement the base `Component` trait defined in `src/component.rs
- **Status**: Implemented with known limitations - **Status**: Implemented with known limitations
#### Intel 4003 Shift Register #### Intel 4003 Shift Register
- **File**: `intel_4003.rs` - **File**: `intel_4003.rs`
- **Features**: - **Features**:
- 10-bit static shift register - 10-bit static shift register
@ -69,12 +76,14 @@ All components implement the base `Component` trait defined in `src/component.rs
- **Status**: Basic implementation - **Status**: Basic implementation
#### Generic Memory Components #### Generic Memory Components
- **GenericRAM**: `generic_ram.rs` - Configurable RAM implementation - **GenericRAM**: `generic_ram.rs` - Configurable RAM implementation
- **GenericROM**: `generic_rom.rs` - Configurable ROM implementation - **GenericROM**: `generic_rom.rs` - Configurable ROM implementation
### Common Functionality (`common/`) ### Common Functionality (`common/`)
#### Intel 400x Series Common Code #### Intel 400x Series Common Code
- **File**: `intel_400x.rs` - **File**: `intel_400x.rs`
- **Purpose**: Shared functionality for all Intel 400x series chips - **Purpose**: Shared functionality for all Intel 400x series chips
- **Features**: - **Features**:
@ -89,6 +98,7 @@ All components implement the base `Component` trait defined in `src/component.rs
### Clock Components (`clock/`) ### Clock Components (`clock/`)
#### Generic Clock Generator #### Generic Clock Generator
- **File**: `generic_clock.rs` - **File**: `generic_clock.rs`
- **Features**: - **Features**:
- Configurable frequency and duty cycle - Configurable frequency and duty cycle
@ -100,6 +110,7 @@ All components implement the base `Component` trait defined in `src/component.rs
## Component Interface ## Component Interface
### Required Methods ### Required Methods
All components must implement: All components must implement:
```rust ```rust
@ -113,6 +124,7 @@ fn is_running(&self) -> bool; // Execution status
``` ```
### Optional Traits ### Optional Traits
Components can implement additional traits for specialized functionality: Components can implement additional traits for specialized functionality:
- **RunnableComponent**: Automatic thread spawning - **RunnableComponent**: Automatic thread spawning
@ -122,12 +134,14 @@ Components can implement additional traits for specialized functionality:
## Pin Configuration ## Pin Configuration
### Standard Pin Types ### Standard Pin Types
- **Data Pins**: `D0-D3` for 4-bit data/address bus - **Data Pins**: `D0-D3` for 4-bit data/address bus
- **Clock Pins**: `PHI1`, `PHI2` for two-phase clock - **Clock Pins**: `PHI1`, `PHI2` for two-phase clock
- **Control Pins**: `SYNC`, `RESET`, `CM`, `CI` for system control - **Control Pins**: `SYNC`, `RESET`, `CM`, `CI` for system control
- **I/O Pins**: `IO0-IO3` for peripheral interface - **I/O Pins**: `IO0-IO3` for peripheral interface
### Pin Signal States ### Pin Signal States
- **High**: Logic level 1 - **High**: Logic level 1
- **Low**: Logic level 0 - **Low**: Logic level 0
- **HighZ**: High impedance (tri-state, not driving) - **HighZ**: High impedance (tri-state, not driving)
@ -135,6 +149,7 @@ Components can implement additional traits for specialized functionality:
## Usage Examples ## Usage Examples
### Creating and Using a Component ### Creating and Using a Component
```rust ```rust
use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::components::memory::intel_4001::Intel4001;
@ -152,6 +167,7 @@ while simulation_running() {
``` ```
### Component Interconnection ### Component Interconnection
```rust ```rust
// Components connect through shared pin references // Components connect through shared pin references
let cpu_pin = cpu.get_pin("D0") ?; let cpu_pin = cpu.get_pin("D0") ?;
@ -165,16 +181,19 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Testing Strategy ## Testing Strategy
### Unit Tests ### Unit Tests
- Located in each component's implementation file - Located in each component's implementation file
- Test individual component behavior in isolation - Test individual component behavior in isolation
- Focus on state management and pin operations - Focus on state management and pin operations
### Integration Tests ### Integration Tests
- Located in `tests/` directory - Located in `tests/` directory
- Test component interactions and system behavior - Test component interactions and system behavior
- Verify proper timing and signal propagation - Verify proper timing and signal propagation
### Mock-Based Tests ### Mock-Based Tests
- Use mock components for controlled testing - Use mock components for controlled testing
- Test error conditions and edge cases - Test error conditions and edge cases
- Verify behavior with various input patterns - Verify behavior with various input patterns
@ -182,6 +201,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Development Guidelines ## Development Guidelines
### Adding New Components ### Adding New Components
1. **Implement Core Traits**: Start with `Component` trait implementation 1. **Implement Core Traits**: Start with `Component` trait implementation
2. **Define Pin Configuration**: Specify required pins and their purposes 2. **Define Pin Configuration**: Specify required pins and their purposes
3. **Implement Timing**: Add clock cycle processing in `update()` method 3. **Implement Timing**: Add clock cycle processing in `update()` method
@ -190,6 +210,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
6. **Update Exports**: Add the component to the appropriate module exports 6. **Update Exports**: Add the component to the appropriate module exports
### Component Design Principles ### Component Design Principles
- **Single Responsibility**: Each component should have one primary function - **Single Responsibility**: Each component should have one primary function
- **Clear Interfaces**: Well-defined pin configurations and behaviors - **Clear Interfaces**: Well-defined pin configurations and behaviors
- **Timing Awareness**: Proper handling of clock cycles and timing constraints - **Timing Awareness**: Proper handling of clock cycles and timing constraints
@ -197,6 +218,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
- **Testability**: Design for easy testing and verification - **Testability**: Design for easy testing and verification
### Performance Considerations ### Performance Considerations
- **Pin Access**: Minimize lock contention on shared pins - **Pin Access**: Minimize lock contention on shared pins
- **State Updates**: Efficient state machine transitions - **State Updates**: Efficient state machine transitions
- **Memory Layout**: Optimize memory access patterns - **Memory Layout**: Optimize memory access patterns
@ -205,6 +227,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Hardware Accuracy ## Hardware Accuracy
### Intel 4004 Series Specifics ### Intel 4004 Series Specifics
- **Two-Phase Clock**: All operations synchronized to Φ1 and Φ2 - **Two-Phase Clock**: All operations synchronized to Φ1 and Φ2
- **4-Bit Architecture**: Data processed in 4-bit nibbles - **4-Bit Architecture**: Data processed in 4-bit nibbles
- **12-Bit Addressing**: 8-bit addresses transferred in two 4-bit cycles - **12-Bit Addressing**: 8-bit addresses transferred in two 4-bit cycles
@ -212,6 +235,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
- **Bus Sharing**: Multiple components share the same data bus - **Bus Sharing**: Multiple components share the same data bus
### Implementation Notes ### Implementation Notes
- Some timing specifications are approximated due to Rust's timing limitations - Some timing specifications are approximated due to Rust's timing limitations
- Pin signal propagation is simplified for simulation performance - Pin signal propagation is simplified for simulation performance
- Bus contention handling prioritizes correctness over perfect hardware accuracy - Bus contention handling prioritizes correctness over perfect hardware accuracy
@ -220,6 +244,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Future Enhancements ## Future Enhancements
### Planned Components ### Planned Components
- Enhanced Intel 4002 RAM with proper refresh cycles - Enhanced Intel 4002 RAM with proper refresh cycles
- Intel 4008/4009 I/O expanders - Intel 4008/4009 I/O expanders
- Intel 4269 programmable ROM - Intel 4269 programmable ROM
@ -227,6 +252,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
- Cycle-accurate instruction execution - Cycle-accurate instruction execution
### Architecture Improvements ### Architecture Improvements
- Better pin signal propagation modeling - Better pin signal propagation modeling
- More sophisticated bus arbitration - More sophisticated bus arbitration
- Enhanced timing constraint checking - Enhanced timing constraint checking

View file

@ -1,10 +1,12 @@
# Intel 400x Common Functionality # Intel 400x Common Functionality
This module provides shared functionality for all Intel 400x series chips (4001, 4002, 4003, 4004). It contains common traits, utilities, and behaviors that are used across multiple components. This module provides shared functionality for all Intel 400x series chips (4001, 4002, 4003, 4004). It contains common
traits, utilities, and behaviors that are used across multiple components.
## Purpose ## Purpose
The Intel 400x series shares many common characteristics: The Intel 400x series shares many common characteristics:
- **Two-phase clock operation** (Φ1, Φ2) - **Two-phase clock operation** (Φ1, Φ2)
- **4-bit data bus** with multiplexed address/data - **4-bit data bus** with multiplexed address/data
- **12-bit address space** (8-bit addresses in two 4-bit cycles) - **12-bit address space** (8-bit addresses in two 4-bit cycles)
@ -61,6 +63,7 @@ pub trait Intel400xClockHandling {
``` ```
**Key Features:** **Key Features:**
- Edge detection for both clock phases - Edge detection for both clock phases
- Previous state tracking for transition detection - Previous state tracking for transition detection
- Thread-safe pin access - Thread-safe pin access
@ -81,6 +84,7 @@ pub trait Intel400xDataBus {
``` ```
**Key Features:** **Key Features:**
- Safe pin access with error handling - Safe pin access with error handling
- Driver name tracking for debugging - Driver name tracking for debugging
- Bus contention avoidance - Bus contention avoidance
@ -101,6 +105,7 @@ pub trait Intel400xAddressHandling {
``` ```
**Key Features:** **Key Features:**
- Two-phase address latching (high nibble, then low nibble) - Two-phase address latching (high nibble, then low nibble)
- Timing-aware address assembly - Timing-aware address assembly
- Access time validation - Access time validation
@ -121,6 +126,7 @@ pub trait Intel400xControlPins {
``` ```
**Key Features:** **Key Features:**
- Safe pin reading with fallback values - Safe pin reading with fallback values
- Consistent pin naming across components - Consistent pin naming across components
@ -139,6 +145,7 @@ pub trait Intel400xResetHandling {
``` ```
**Key Features:** **Key Features:**
- System-wide reset signal handling - System-wide reset signal handling
- Component-specific reset behaviors - Component-specific reset behaviors
@ -167,6 +174,7 @@ pub trait Intel400xTimingState {
``` ```
**Key Features:** **Key Features:**
- Unified state machine interface - Unified state machine interface
- Timing constraint management - Timing constraint management
- Address tracking and validation - Address tracking and validation
@ -243,6 +251,7 @@ pub mod utils {
### Error Handling ### Error Handling
All pin operations include proper error handling: All pin operations include proper error handling:
- **Lock failures**: Return safe default values - **Lock failures**: Return safe default values
- **Pin not found**: Return appropriate error messages - **Pin not found**: Return appropriate error messages
- **Invalid states**: Graceful degradation - **Invalid states**: Graceful degradation
@ -394,4 +403,5 @@ The common functionality depends on:
- **Shared Code**: No duplication across components - **Shared Code**: No duplication across components
- **Efficient State Storage**: Compact state representation - **Efficient State Storage**: Compact state representation
This common functionality module provides a solid foundation for all Intel 400x components while maintaining high performance and hardware accuracy. This common functionality module provides a solid foundation for all Intel 400x components while maintaining high
performance and hardware accuracy.

View file

@ -3,10 +3,10 @@
//! This module provides shared timing, clock handling, and bus operations //! This module provides shared timing, clock handling, and bus operations
//! used across all Intel 400x family components (4001, 4002, 4003, 4004). //! used across all Intel 400x family components (4001, 4002, 4003, 4004).
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component}; use crate::component::{BaseComponent, Component};
use crate::pin::{Pin, PinValue}; use crate::pin::{Pin, PinValue};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
// Memory operation state machine states (shared across all 400x chips) // Memory operation state machine states (shared across all 400x chips)
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
@ -17,7 +17,6 @@ pub enum MemoryState {
DriveData, // Latency elapsed, driving data on bus DriveData, // Latency elapsed, driving data on bus
} }
/// Common timing constants for Intel 400x series /// Common timing constants for Intel 400x series
pub struct TimingConstants; pub struct TimingConstants;
@ -121,7 +120,10 @@ pub trait Intel400xDataBus {
} else { } else {
PinValue::Low PinValue::Low
}; };
pin_guard.set_driver(Some(format!("{}_DATA", self.get_base().get_name())), pin_value); pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())),
pin_value,
);
} }
} }
} }
@ -132,7 +134,10 @@ pub trait Intel400xDataBus {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(format!("{}_DATA", self.get_base().get_name())), PinValue::HighZ); pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())),
PinValue::HighZ,
);
} }
} }
} }
@ -146,7 +151,11 @@ pub trait Intel400xAddressHandling {
/// Assemble complete 8-bit address from high and low nibbles /// Assemble complete 8-bit address from high and low nibbles
/// Hardware: Intel 4004 provides address in two 4-bit phases /// Hardware: Intel 4004 provides address in two 4-bit phases
/// Format: (high_nibble << 4) | low_nibble /// Format: (high_nibble << 4) | low_nibble
fn assemble_full_address(&self, high_nibble: Option<u8>, low_nibble: Option<u8>) -> Option<u16> { fn assemble_full_address(
&self,
high_nibble: Option<u8>,
low_nibble: Option<u8>,
) -> Option<u16> {
if let (Some(high), Some(low)) = (high_nibble, low_nibble) { if let (Some(high), Some(low)) = (high_nibble, low_nibble) {
Some(((high as u16) << 4) | (low as u16)) Some(((high as u16) << 4) | (low as u16))
} else { } else {
@ -163,7 +172,7 @@ pub trait Intel400xAddressHandling {
address_low_nibble: &mut Option<u8>, address_low_nibble: &mut Option<u8>,
full_address_ready: &mut bool, full_address_ready: &mut bool,
address_latch_time: &mut Option<Instant>, address_latch_time: &mut Option<Instant>,
access_time: Duration, _access_time: Duration,
) { ) {
if address_high_nibble.is_none() { if address_high_nibble.is_none() {
// First cycle: latch high nibble (bits 7-4) // First cycle: latch high nibble (bits 7-4)
@ -173,7 +182,9 @@ pub trait Intel400xAddressHandling {
*address_low_nibble = Some(nibble); *address_low_nibble = Some(nibble);
// Try to assemble full address // Try to assemble full address
if let Some(address) = self.assemble_full_address(*address_high_nibble, *address_low_nibble) { if let Some(_address) =
self.assemble_full_address(*address_high_nibble, *address_low_nibble)
{
*full_address_ready = true; *full_address_ready = true;
*address_latch_time = Some(Instant::now()); *address_latch_time = Some(Instant::now());
@ -186,7 +197,11 @@ pub trait Intel400xAddressHandling {
/// Handle latency timing during wait state /// Handle latency timing during wait state
/// Returns: true if latency has elapsed /// Returns: true if latency has elapsed
fn handle_latency_wait(&self, address_latch_time: &Option<Instant>, access_time: Duration) -> bool { fn handle_latency_wait(
&self,
address_latch_time: &Option<Instant>,
access_time: Duration,
) -> bool {
if let Some(latch_time) = address_latch_time { if let Some(latch_time) = address_latch_time {
latch_time.elapsed() >= access_time latch_time.elapsed() >= access_time
} else { } else {
@ -214,7 +229,8 @@ pub trait Intel400xControlPins {
/// Read CM-ROM pin state /// Read CM-ROM pin state
fn read_cm_rom_pin(&self) -> bool { fn read_cm_rom_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("CM") { // Note: CM pin name varies by component if let Ok(pin) = self.get_base().get_pin("CM") {
// Note: CM pin name varies by component
if let Ok(pin_guard) = pin.lock() { if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else { } else {

View file

@ -462,7 +462,8 @@ impl Intel4004 {
/// Hardware: RESET pin clears all internal state and tri-states outputs /// Hardware: RESET pin clears all internal state and tri-states outputs
fn handle_reset(&mut self) { fn handle_reset(&mut self) {
let (_, _, _, test) = self.read_control_pins(); let (_, _, _, test) = self.read_control_pins();
if test { // Note: Using TEST pin as RESET for now - should be RESET pin if test {
// Note: Using TEST pin as RESET for now - should be RESET pin
// RESET is high - clear all internal state // RESET is high - clear all internal state
self.accumulator = 0; self.accumulator = 0;
self.carry = false; self.carry = false;
@ -575,7 +576,7 @@ impl Intel4004 {
fn assemble_full_address(&mut self) { fn assemble_full_address(&mut self) {
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) { if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
// Assemble 8-bit address: (high << 4) | low // Assemble 8-bit address: (high << 4) | low
self.address_latch = ((high as u8) << 4) | (low as u8); self.address_latch = (high << 4) | low;
self.full_address_ready = true; self.full_address_ready = true;
self.address_latch_time = Some(Instant::now()); self.address_latch_time = Some(Instant::now());
@ -699,7 +700,6 @@ impl Intel4004 {
// Register Reference Instructions (0x60-0x6F) // Register Reference Instructions (0x60-0x6F)
0x60..=0x6F => { 0x60..=0x6F => {
let reg = opcode & 0x0F;
match opcode { match opcode {
0x60..=0x67 => Instruction::Wrr, // WRR 0x60..=0x67 => Instruction::Wrr, // WRR
0x68..=0x6F => Instruction::Wpm, // WPM 0x68..=0x6F => Instruction::Wpm, // WPM
@ -839,7 +839,9 @@ impl Intel4004 {
Instruction::Sub(reg) => { Instruction::Sub(reg) => {
if reg < 16 { if reg < 16 {
let result = self.accumulator.wrapping_sub(self.index_registers[reg as usize]); let result = self
.accumulator
.wrapping_sub(self.index_registers[reg as usize]);
self.carry = self.accumulator < self.index_registers[reg as usize]; self.carry = self.accumulator < self.index_registers[reg as usize];
self.accumulator = result & 0x0F; self.accumulator = result & 0x0F;
} }
@ -860,8 +862,12 @@ impl Intel4004 {
Instruction::SubC(reg) => { Instruction::SubC(reg) => {
if reg < 16 { if reg < 16 {
let carry_val = if self.carry { 1 } else { 0 }; let carry_val = if self.carry { 1 } else { 0 };
let result = self.accumulator.wrapping_sub(self.index_registers[reg as usize]).wrapping_sub(carry_val); let result = self
self.carry = self.accumulator < (self.index_registers[reg as usize] + carry_val); .accumulator
.wrapping_sub(self.index_registers[reg as usize])
.wrapping_sub(carry_val);
self.carry =
self.accumulator < (self.index_registers[reg as usize] + carry_val);
self.accumulator = result & 0x0F; self.accumulator = result & 0x0F;
} }
self.program_counter.inc(); self.program_counter.inc();
@ -870,14 +876,16 @@ impl Intel4004 {
// Logic Instructions // Logic Instructions
Instruction::Ral => { Instruction::Ral => {
let new_carry = (self.accumulator & 0x08) != 0; let new_carry = (self.accumulator & 0x08) != 0;
self.accumulator = ((self.accumulator << 1) | (if self.carry { 1 } else { 0 })) & 0x0F; self.accumulator =
((self.accumulator << 1) | (if self.carry { 1 } else { 0 })) & 0x0F;
self.carry = new_carry; self.carry = new_carry;
self.program_counter.inc(); self.program_counter.inc();
} }
Instruction::Rar => { Instruction::Rar => {
let new_carry = (self.accumulator & 0x01) != 0; let new_carry = (self.accumulator & 0x01) != 0;
self.accumulator = ((self.accumulator >> 1) | (if self.carry { 0x08 } else { 0 })) & 0x0F; self.accumulator =
((self.accumulator >> 1) | (if self.carry { 0x08 } else { 0 })) & 0x0F;
self.carry = new_carry; self.carry = new_carry;
self.program_counter.inc(); self.program_counter.inc();
} }
@ -1042,7 +1050,8 @@ impl Intel4004 {
// Increment Register Instructions // Increment Register Instructions
Instruction::Inc(reg) => { Instruction::Inc(reg) => {
if reg < 16 { if reg < 16 {
self.index_registers[reg as usize] = (self.index_registers[reg as usize] + 1) & 0x0F; self.index_registers[reg as usize] =
(self.index_registers[reg as usize] + 1) & 0x0F;
} }
self.program_counter.inc(); self.program_counter.inc();
} }

View file

@ -5,9 +5,8 @@ use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component, RunnableComponent}; use crate::component::{BaseComponent, Component, RunnableComponent};
use crate::components::common::intel_400x::{ use crate::components::common::intel_400x::{
Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins, Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins, Intel400xDataBus,
Intel400xDataBus, Intel400xResetHandling, Intel400xTimingState, Intel400xResetHandling, Intel400xTimingState, MemoryState, TimingState,
MemoryState, TimingState
}; };
use crate::pin::{Pin, PinValue}; use crate::pin::{Pin, PinValue};
@ -49,7 +48,6 @@ enum IoMode {
Bidirectional, // I/O pins bidirectional (not used in 4001) Bidirectional, // I/O pins bidirectional (not used in 4001)
} }
impl Intel400xClockHandling for Intel4001 { impl Intel400xClockHandling for Intel4001 {
fn get_base(&self) -> &BaseComponent { fn get_base(&self) -> &BaseComponent {
&self.base &self.base
@ -396,8 +394,12 @@ impl Intel4001 {
/// Hardware-accurate: Φ2 is when peripherals drive data, so we handle data driving /// Hardware-accurate: Φ2 is when peripherals drive data, so we handle data driving
/// Focus: Data driving operations /// Focus: Data driving operations
fn handle_memory_data_operations(&mut self) { fn handle_memory_data_operations(&mut self) {
println!("DEBUG: {} - handle_memory_data_operations: state={:?}, address_ready={}", println!(
self.base.name(), self.memory_state, self.full_address_ready); "DEBUG: {} - handle_memory_data_operations: state={:?}, address_ready={}",
self.base.name(),
self.memory_state,
self.full_address_ready
);
match self.memory_state { match self.memory_state {
MemoryState::Idle => { MemoryState::Idle => {
// During data phase, idle state means tri-state the bus // During data phase, idle state means tri-state the bus
@ -408,28 +410,43 @@ impl Intel4001 {
MemoryState::AddressPhase => { MemoryState::AddressPhase => {
// Address phase should be handled by Φ1, not Φ2 // Address phase should be handled by Φ1, not Φ2
// Tri-state bus during wrong phase // Tri-state bus during wrong phase
println!("DEBUG: {} - In AddressPhase during Φ2, tri-stating", self.base.name()); println!(
"DEBUG: {} - In AddressPhase during Φ2, tri-stating",
self.base.name()
);
self.tri_state_data_bus(); self.tri_state_data_bus();
} }
MemoryState::WaitLatency => { MemoryState::WaitLatency => {
// Address latched, waiting for access latency // Address latched, waiting for access latency
// Check if latency has elapsed and we can transition to data phase // Check if latency has elapsed and we can transition to data phase
println!("DEBUG: {} - In WaitLatency, checking latency", self.base.name()); println!(
"DEBUG: {} - In WaitLatency, checking latency",
self.base.name()
);
self.handle_latency_wait(); self.handle_latency_wait();
// If we transitioned to DriveData, handle data driving // If we transitioned to DriveData, handle data driving
if self.memory_state == MemoryState::DriveData { if self.memory_state == MemoryState::DriveData {
println!("DEBUG: {} - Transitioned to DriveData, calling handle_data_driving", self.base.name()); println!(
"DEBUG: {} - Transitioned to DriveData, calling handle_data_driving",
self.base.name()
);
self.handle_data_driving(); self.handle_data_driving();
} else { } else {
println!("DEBUG: {} - Still in WaitLatency after handle_latency_wait", self.base.name()); println!(
"DEBUG: {} - Still in WaitLatency after handle_latency_wait",
self.base.name()
);
} }
} }
MemoryState::DriveData => { MemoryState::DriveData => {
// Latency elapsed, drive data on bus during Φ2 // Latency elapsed, drive data on bus during Φ2
// Data will remain on bus until Φ2 falling edge // Data will remain on bus until Φ2 falling edge
println!("DEBUG: {} - In DriveData state, calling handle_data_driving", self.base.name()); println!(
"DEBUG: {} - In DriveData state, calling handle_data_driving",
self.base.name()
);
self.handle_data_driving(); self.handle_data_driving();
} }
} }
@ -474,7 +491,9 @@ impl Intel4001 {
} else if self.address_low_nibble.is_none() { } else if self.address_low_nibble.is_none() {
// Second cycle: latch low nibble (bits 3-0) and transition to latency wait // Second cycle: latch low nibble (bits 3-0) and transition to latency wait
self.address_low_nibble = Some(nibble); self.address_low_nibble = Some(nibble);
if let Some(address) = self.assemble_full_address(self.address_high_nibble, self.address_low_nibble) { if let Some(address) =
self.assemble_full_address(self.address_high_nibble, self.address_low_nibble)
{
self.last_address = address; self.last_address = address;
self.start_latency_wait(); self.start_latency_wait();
} }
@ -493,16 +512,27 @@ impl Intel4001 {
fn handle_latency_wait(&mut self) { fn handle_latency_wait(&mut self) {
if let Some(latch_time) = self.address_latch_time { if let Some(latch_time) = self.address_latch_time {
let elapsed = latch_time.elapsed(); let elapsed = latch_time.elapsed();
println!("DEBUG: {} - handle_latency_wait: elapsed={:?}, access_time={:?}, ready={}", println!(
self.base.get_name(), elapsed, self.access_time, self.full_address_ready); "DEBUG: {} - handle_latency_wait: elapsed={:?}, access_time={:?}, ready={}",
self.base.get_name(),
elapsed,
self.access_time,
self.full_address_ready
);
if elapsed >= self.access_time { if elapsed >= self.access_time {
// Latency elapsed, transition to data driving // Latency elapsed, transition to data driving
// Data will be driven on next Φ2 rising edge // Data will be driven on next Φ2 rising edge
println!("DEBUG: {} - Latency elapsed, transitioning to DriveData", self.base.get_name()); println!(
"DEBUG: {} - Latency elapsed, transitioning to DriveData",
self.base.get_name()
);
self.start_data_driving(); self.start_data_driving();
} }
} else { } else {
println!("DEBUG: {} - handle_latency_wait: no latch_time set", self.base.get_name()); println!(
"DEBUG: {} - handle_latency_wait: no latch_time set",
self.base.get_name()
);
} }
} }
@ -520,8 +550,14 @@ impl Intel4001 {
let chip_select = self.read_cm_rom_pin(); let chip_select = self.read_cm_rom_pin();
let io_select = self.read_reset_pin(); // Using reset pin as CI for now let io_select = self.read_reset_pin(); // Using reset pin as CI for now
println!("DEBUG: {} - handle_data_driving: SYNC={}, CM={}, CI={}, Address_Ready={}", println!(
self.base.name(), sync, chip_select, io_select, self.full_address_ready); "DEBUG: {} - handle_data_driving: SYNC={}, CM={}, CI={}, Address_Ready={}",
self.base.name(),
sync,
chip_select,
io_select,
self.full_address_ready
);
// Memory read: CM=1 (chip_select), CI=0 (!io_select), valid address // Memory read: CM=1 (chip_select), CI=0 (!io_select), valid address
if sync && chip_select && !io_select && self.full_address_ready { if sync && chip_select && !io_select && self.full_address_ready {
@ -530,12 +566,21 @@ impl Intel4001 {
let address = self.last_address; let address = self.last_address;
if (address as usize) < self.memory.len() { if (address as usize) < self.memory.len() {
let data = self.memory[address as usize]; let data = self.memory[address as usize];
println!("DEBUG: {} - All conditions met, driving data 0x{:x} to address 0x{:x}", self.base.name(), data, address); println!(
"DEBUG: {} - All conditions met, driving data 0x{:x} to address 0x{:x}",
self.base.name(),
data,
address
);
self.write_data_bus(data); self.write_data_bus(data);
// Note: Don't call return_to_idle() here - wait for Φ2 falling edge // Note: Don't call return_to_idle() here - wait for Φ2 falling edge
} else { } else {
// Invalid address, tri-state // Invalid address, tri-state
println!("DEBUG: {} - Invalid address 0x{:x}, tri-stating", self.base.name(), address); println!(
"DEBUG: {} - Invalid address 0x{:x}, tri-stating",
self.base.name(),
address
);
self.tri_state_data_bus(); self.tri_state_data_bus();
} }
} else { } else {
@ -682,9 +727,16 @@ impl Intel4001 {
/// Debug function to log state transitions for troubleshooting /// Debug function to log state transitions for troubleshooting
/// Parameters: test_name - Name of the test for context /// Parameters: test_name - Name of the test for context
pub fn debug_state_transitions(&self, test_name: &str) { pub fn debug_state_transitions(&self, test_name: &str) {
println!("{} - State: {:?}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}, Latch time: {:?}", println!(
test_name, self.memory_state, self.address_high_nibble, self.address_low_nibble, "{} - State: {:?}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}, Latch time: {:?}",
self.last_address, self.full_address_ready, self.address_latch_time); test_name,
self.memory_state,
self.address_high_nibble,
self.address_low_nibble,
self.last_address,
self.full_address_ready,
self.address_latch_time
);
} }
} }
@ -886,7 +938,7 @@ mod tests {
assert_eq!(rom.memory_state, MemoryState::WaitLatency); assert_eq!(rom.memory_state, MemoryState::WaitLatency);
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
} }
#[test] #[test]
@ -1024,7 +1076,7 @@ mod tests {
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
rom.debug_state_transitions("BEFORE_SLEEP"); rom.debug_state_transitions("BEFORE_SLEEP");
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
rom.debug_state_transitions("AFTER_SLEEP"); rom.debug_state_transitions("AFTER_SLEEP");
// --- Data phase --- // --- Data phase ---
@ -1049,14 +1101,20 @@ mod tests {
// Debug: Check what data the ROM thinks it should drive // Debug: Check what data the ROM thinks it should drive
let address = rom.last_address; let address = rom.last_address;
let expected_data = rom.memory[address as usize]; let expected_data = rom.memory[address as usize];
println!("DEBUG: ROM address 0x{:x}, data at address: 0x{:x}", address, expected_data); println!(
"DEBUG: ROM address 0x{:x}, data at address: 0x{:x}",
address, expected_data
);
// Debug: Check the bit extraction logic // Debug: Check the bit extraction logic
let bit0 = (expected_data >> 0) & 1; let bit0 = (expected_data >> 0) & 1;
let bit1 = (expected_data >> 1) & 1; let bit1 = (expected_data >> 1) & 1;
let bit2 = (expected_data >> 2) & 1; let bit2 = (expected_data >> 2) & 1;
let bit3 = (expected_data >> 3) & 1; let bit3 = (expected_data >> 3) & 1;
println!("DEBUG: Bit extraction - bit0: {}, bit1: {}, bit2: {}, bit3: {}", bit0, bit1, bit2, bit3); println!(
"DEBUG: Bit extraction - bit0: {}, bit1: {}, bit2: {}, bit3: {}",
bit0, bit1, bit2, bit3
);
// Debug: Check actual pin states and settlement status // Debug: Check actual pin states and settlement status
let d0_actual = d0_pin.lock().unwrap().read(); let d0_actual = d0_pin.lock().unwrap().read();
@ -1074,10 +1132,22 @@ mod tests {
let d2_settled = d2_pin.lock().unwrap().is_settled(); let d2_settled = d2_pin.lock().unwrap().is_settled();
let d3_settled = d3_pin.lock().unwrap().is_settled(); let d3_settled = d3_pin.lock().unwrap().is_settled();
println!("DEBUG: D0 actual: {:?} (bit 0: {}), immediate: {:?}, settled: {}", d0_actual, bit0, d0_immediate, d0_settled); println!(
println!("DEBUG: D1 actual: {:?} (bit 1: {}), immediate: {:?}, settled: {}", d1_actual, bit1, d1_immediate, d1_settled); "DEBUG: D0 actual: {:?} (bit 0: {}), immediate: {:?}, settled: {}",
println!("DEBUG: D2 actual: {:?} (bit 2: {}), immediate: {:?}, settled: {}", d2_actual, bit2, d2_immediate, d2_settled); d0_actual, bit0, d0_immediate, d0_settled
println!("DEBUG: D3 actual: {:?} (bit 3: {}), immediate: {:?}, settled: {}", d3_actual, bit3, d3_immediate, d3_settled); );
println!(
"DEBUG: D1 actual: {:?} (bit 1: {}), immediate: {:?}, settled: {}",
d1_actual, bit1, d1_immediate, d1_settled
);
println!(
"DEBUG: D2 actual: {:?} (bit 2: {}), immediate: {:?}, settled: {}",
d2_actual, bit2, d2_immediate, d2_settled
);
println!(
"DEBUG: D3 actual: {:?} (bit 3: {}), immediate: {:?}, settled: {}",
d3_actual, bit3, d3_immediate, d3_settled
);
// Debug: Check actual pin states immediately after data driving // Debug: Check actual pin states immediately after data driving
let d0_actual = d0_pin.lock().unwrap().read(); let d0_actual = d0_pin.lock().unwrap().read();
@ -1085,7 +1155,10 @@ mod tests {
let d2_actual = d2_pin.lock().unwrap().read(); let d2_actual = d2_pin.lock().unwrap().read();
let d3_actual = d3_pin.lock().unwrap().read(); let d3_actual = d3_pin.lock().unwrap().read();
println!("DEBUG: After data driving - D0: {:?}, D1: {:?}, D2: {:?}, D3: {:?}", d0_actual, d1_actual, d2_actual, d3_actual); println!(
"DEBUG: After data driving - D0: {:?}, D1: {:?}, D2: {:?}, D3: {:?}",
d0_actual, d1_actual, d2_actual, d3_actual
);
// Debug: Check what the ROM thinks it should be driving // Debug: Check what the ROM thinks it should be driving
let expected_data = 0x12; let expected_data = 0x12;
@ -1093,14 +1166,17 @@ mod tests {
let bit1 = (expected_data >> 1) & 1; let bit1 = (expected_data >> 1) & 1;
let bit2 = (expected_data >> 2) & 1; let bit2 = (expected_data >> 2) & 1;
let bit3 = (expected_data >> 3) & 1; let bit3 = (expected_data >> 3) & 1;
println!("DEBUG: Expected bits - bit0: {}, bit1: {}, bit2: {}, bit3: {}", bit0, bit1, bit2, bit3); println!(
"DEBUG: Expected bits - bit0: {}, bit1: {}, bit2: {}, bit3: {}",
bit0, bit1, bit2, bit3
);
// Verify data is driven on bus while Φ2 is high: 0x12 = 0001 0010 // Verify data is driven on bus while Φ2 is high: 0x12 = 0001 0010
// 0x12 = 18 decimal = 0001 0010 binary // 0x12 = 18 decimal = 0001 0010 binary
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=0 // For 4-bit data bus: D3=0, D2=0, D1=1, D0=0
// Small delay to allow pin state to settle // Small delay to allow pin state to settle
std::thread::sleep(Duration::from_micros(1)); thread::sleep(Duration::from_micros(1));
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0 assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1 assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1
@ -1198,7 +1274,7 @@ mod tests {
rom.update(); // falling edge rom.update(); // falling edge
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
// --- Data phase --- // --- Data phase ---
phi2_pin phi2_pin
@ -1307,7 +1383,7 @@ mod tests {
rom.update(); // rising edge for low nibble rom.update(); // rising edge for low nibble
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
// Data phase // Data phase
phi2_pin phi2_pin
@ -1383,7 +1459,7 @@ mod tests {
rom.update(); // rising edge for low nibble rom.update(); // rising edge for low nibble
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
// Data phase // Data phase
phi2_pin phi2_pin
@ -1419,10 +1495,6 @@ mod tests {
let sync_pin = rom.get_pin("SYNC").unwrap(); let sync_pin = rom.get_pin("SYNC").unwrap();
let cm_pin = rom.get_pin("CM").unwrap(); let cm_pin = rom.get_pin("CM").unwrap();
let ci_pin = rom.get_pin("CI").unwrap(); let ci_pin = rom.get_pin("CI").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 up memory read operation: SYNC=1, chip_select=1, io_select=0 (memory access) // Set up memory read operation: SYNC=1, chip_select=1, io_select=0 (memory access)
{ {
@ -1740,7 +1812,7 @@ mod tests {
rom.update(); // rising edge for low nibble rom.update(); // rising edge for low nibble
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
// Data phase // Data phase
phi2_pin phi2_pin
@ -1815,7 +1887,7 @@ mod tests {
rom.update(); // rising edge for low nibble rom.update(); // rising edge for low nibble
// Advance simulated time to exceed access_time (1ns) // Advance simulated time to exceed access_time (1ns)
std::thread::sleep(Duration::from_nanos(2)); thread::sleep(Duration::from_nanos(2));
// Data phase // Data phase
phi2_pin phi2_pin

View file

@ -4,12 +4,12 @@ use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component, RunnableComponent}; use crate::component::{BaseComponent, Component, RunnableComponent};
use crate::pin::{Pin, PinValue};
use crate::components::common::intel_400x::{ use crate::components::common::intel_400x::{
Intel400xClockHandling, Intel400xDataBus, Intel400xAddressHandling, Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins,
Intel400xControlPins, Intel400xResetHandling, Intel400xTimingState, Intel400xDataBus, Intel400xResetHandling, Intel400xTimingState, RamState,
TimingState, MemoryState, RamState, utils TimingState,
}; };
use crate::pin::{Pin, PinValue};
/// Intel 4002 - 320-bit RAM (80 nibbles × 4 bits) with integrated output ports /// Intel 4002 - 320-bit RAM (80 nibbles × 4 bits) with integrated output ports
/// Part of the MCS-4 family, designed to work with Intel 4004 CPU /// Part of the MCS-4 family, designed to work with Intel 4004 CPU
@ -53,7 +53,6 @@ pub struct Intel4002 {
current_instruction: u8, // Current instruction being processed current_instruction: u8, // Current instruction being processed
} }
/// Intel 4002 RAM variants /// Intel 4002 RAM variants
/// - 4002-1: Standard RAM variant /// - 4002-1: Standard RAM variant
/// - 4002-2: RAM variant with different configuration /// - 4002-2: RAM variant with different configuration
@ -173,7 +172,11 @@ impl Intel4002 {
/// Create a new Intel 4002 RAM with specified variant and access time /// Create a new Intel 4002 RAM with specified variant and access time
/// Parameters: name - Component identifier, variant - RAM variant (Type1 or Type2), access_time_ns - Memory access time in nanoseconds /// Parameters: name - Component identifier, variant - RAM variant (Type1 or Type2), access_time_ns - Memory access time in nanoseconds
/// Returns: New Intel4002 instance with configurable variant and access timing /// Returns: New Intel4002 instance with configurable variant and access timing
pub fn new_with_variant_and_access_time(name: String, variant: RamVariant, access_time_ns: u64) -> Self { pub fn new_with_variant_and_access_time(
name: String,
variant: RamVariant,
access_time_ns: u64,
) -> Self {
// Intel 4002 pinout (based on MCS-4 architecture): // Intel 4002 pinout (based on MCS-4 architecture):
// - 4 data pins (D0-D3) for multiplexed address/data // - 4 data pins (D0-D3) for multiplexed address/data
// - 4 output port pins (O0-O3) // - 4 output port pins (O0-O3)
@ -312,7 +315,8 @@ impl Intel4002 {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ); pin_guard
.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
} }
} }
} }
@ -324,13 +328,15 @@ impl Intel4002 {
for port in 0..4 { for port in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("O{}", port)) { if let Ok(pin) = self.base.get_pin(&format!("O{}", port)) {
if let Ok(mut pin_guard) = pin.lock() { if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.name())), PinValue::HighZ); pin_guard.set_driver(
Some(format!("{}_OUTPUT", self.base.name())),
PinValue::HighZ,
);
} }
} }
} }
} }
/// Read the two-phase clock pins from CPU /// Read the two-phase clock pins from CPU
/// Returns: (PHI1_value, PHI2_value) /// Returns: (PHI1_value, PHI2_value)
/// Hardware: Intel 4004 provides two-phase clock for synchronization /// Hardware: Intel 4004 provides two-phase clock for synchronization
@ -358,11 +364,6 @@ impl Intel4002 {
(phi1, phi2) (phi1, phi2)
} }
fn is_phi1_rising_edge(&self) -> bool {
let (phi1, _) = self.read_clock_pins();
phi1 == PinValue::High && self.prev_phi1 == PinValue::Low
}
fn is_phi1_falling_edge(&self) -> bool { fn is_phi1_falling_edge(&self) -> bool {
let (phi1, _) = self.read_clock_pins(); let (phi1, _) = self.read_clock_pins();
phi1 == PinValue::Low && self.prev_phi1 == PinValue::High phi1 == PinValue::Low && self.prev_phi1 == PinValue::High
@ -464,7 +465,7 @@ impl Intel4002 {
fn assemble_full_address(&mut self) { fn assemble_full_address(&mut self) {
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) { if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
// Assemble 8-bit address: (high << 4) | low // Assemble 8-bit address: (high << 4) | low
self.last_address = ((high as u8) << 4) | (low as u8); self.last_address = (high << 4) | low;
self.full_address_ready = true; self.full_address_ready = true;
self.address_latch_time = Some(Instant::now()); self.address_latch_time = Some(Instant::now());
@ -833,9 +834,9 @@ impl Intel4002 {
/// Handle instruction cycle synchronization /// Handle instruction cycle synchronization
fn handle_instruction_cycle(&mut self) { fn handle_instruction_cycle(&mut self) {
let (sync, _cm, p0, _) = self.read_control_pins(); let (sync, _cm, _p0, _) = self.read_control_pins();
if sync && self.is_phi1_rising_edge() { if sync && self.is_phi1_rising_edge(self.prev_phi1) {
// Start of new instruction cycle // Start of new instruction cycle
self.instruction_phase = true; self.instruction_phase = true;
self.current_instruction = self.read_data_bus(); self.current_instruction = self.read_data_bus();
@ -1094,9 +1095,16 @@ impl Intel4002 {
/// Debug function to log state transitions for troubleshooting /// Debug function to log state transitions for troubleshooting
/// Parameters: test_name - Name of the test for context /// Parameters: test_name - Name of the test for context
pub fn debug_state_transitions(&self, test_name: &str) { pub fn debug_state_transitions(&self, test_name: &str) {
println!("{} - State: {:?}, Bank: {}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}", println!(
test_name, self.ram_state, self.bank_select, self.address_high_nibble, "{} - State: {:?}, Bank: {}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}",
self.address_low_nibble, self.last_address, self.full_address_ready); test_name,
self.ram_state,
self.bank_select,
self.address_high_nibble,
self.address_low_nibble,
self.last_address,
self.full_address_ready
);
} }
/// Setup helper for instruction testing /// Setup helper for instruction testing
@ -1104,12 +1112,6 @@ impl Intel4002 {
self.current_instruction = instruction; self.current_instruction = instruction;
self.instruction_phase = true; self.instruction_phase = true;
} }
/// Tri-state all outputs (comprehensive tri-state)
fn tri_state_all_outputs(&self) {
self.tri_state_data_bus();
self.tri_state_output_ports();
}
} }
// Custom formatter for debugging // Custom formatter for debugging
@ -1272,9 +1274,15 @@ mod tests {
} }
// High nibble // High nibble
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::High); phi1_pin
.lock()
.unwrap()
.set_driver(Some("TEST".into()), PinValue::High);
ram.update(); // rising edge -> latch high nibble ram.update(); // rising edge -> latch high nibble
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::Low); phi1_pin
.lock()
.unwrap()
.set_driver(Some("TEST".into()), PinValue::Low);
ram.update(); // falling edge ram.update(); // falling edge
// Should have transitioned to AddressPhase // Should have transitioned to AddressPhase
@ -1294,9 +1302,15 @@ mod tests {
} }
// Low nibble // Low nibble
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::High); phi1_pin
.lock()
.unwrap()
.set_driver(Some("TEST".into()), PinValue::High);
ram.update(); // rising edge -> latch low nibble ram.update(); // rising edge -> latch low nibble
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::Low); phi1_pin
.lock()
.unwrap()
.set_driver(Some("TEST".into()), PinValue::Low);
ram.update(); // falling edge ram.update(); // falling edge
// Should have assembled full address and transitioned to WaitLatency // Should have assembled full address and transitioned to WaitLatency
@ -1452,7 +1466,7 @@ mod tests {
#[test] #[test]
fn test_ram_state_transitions() { fn test_ram_state_transitions() {
let mut ram = Intel4002::new_with_access_time("RAM_4002".to_string(), 1); let ram = Intel4002::new_with_access_time("RAM_4002".to_string(), 1);
// Initially should be in idle state // Initially should be in idle state
assert_eq!(ram.ram_state, RamState::Idle); assert_eq!(ram.ram_state, RamState::Idle);

View file

@ -167,7 +167,8 @@ impl Intel4003 {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ); pin_guard
.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
} }
} }
} }
@ -178,7 +179,10 @@ impl Intel4003 {
for i in 0..10 { for i in 0..10 {
if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.name())), PinValue::HighZ); pin_guard.set_driver(
Some(format!("{}_OUTPUT", self.base.name())),
PinValue::HighZ,
);
} }
} }
} }
@ -274,7 +278,7 @@ impl Intel4003 {
/// Assemble complete 8-bit address from high and low nibbles /// Assemble complete 8-bit address from high and low nibbles
fn assemble_full_address(&mut self) { fn assemble_full_address(&mut self) {
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) { if let (Some(_high), Some(_low)) = (self.address_high_nibble, self.address_low_nibble) {
// Assemble 8-bit address: (high << 4) | low // Assemble 8-bit address: (high << 4) | low
self.full_address_ready = true; self.full_address_ready = true;
self.address_latch_time = Some(Instant::now()); self.address_latch_time = Some(Instant::now());
@ -400,7 +404,10 @@ impl Intel4003 {
if sync && chip_select && self.full_address_ready { if sync && chip_select && self.full_address_ready {
// Read serial input data from data bus // Read serial input data from data bus
let serial_data = self.read_data_bus(); let serial_data = self.read_data_bus();
println!("DEBUG: 4003 shift operation - serial_data = 0x{:x}", serial_data); println!(
"DEBUG: 4003 shift operation - serial_data = 0x{:x}",
serial_data
);
// Shift in the new bits (serial input) // Shift in the new bits (serial input)
// For 4003, we shift in 4 bits at a time // For 4003, we shift in 4 bits at a time
@ -413,16 +420,25 @@ impl Intel4003 {
for i in 0..4 { for i in 0..4 {
let bit = (serial_data >> i) & 1; let bit = (serial_data >> i) & 1;
self.shift_register[i] = bit; self.shift_register[i] = bit;
println!("DEBUG: 4003 shift step {} - inserted bit {} at position {}", i, bit, i); println!(
"DEBUG: 4003 shift step {} - inserted bit {} at position {}",
i, bit, i
);
} }
// Update output latch with new shift register contents // Update output latch with new shift register contents
self.output_latch.copy_from_slice(&self.shift_register); self.output_latch.copy_from_slice(&self.shift_register);
self.update_output_pins(); self.update_output_pins();
println!("DEBUG: 4003 shift register after operation: {:?}", self.shift_register); println!(
"DEBUG: 4003 shift register after operation: {:?}",
self.shift_register
);
let (high, low) = self.get_shift_register(); let (high, low) = self.get_shift_register();
println!("DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}", high, low); println!(
"DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}",
high, low
);
self.shift_state = ShiftState::OutputData; self.shift_state = ShiftState::OutputData;
} else { } else {
@ -436,7 +452,6 @@ impl Intel4003 {
// This state maintains the output until next operation // This state maintains the output until next operation
} }
/// Get the current shift register value /// Get the current shift register value
/// Returns: 10-bit value as a tuple (high_byte, low_2_bits) /// Returns: 10-bit value as a tuple (high_byte, low_2_bits)
pub fn get_shift_register(&self) -> (u8, u8) { pub fn get_shift_register(&self) -> (u8, u8) {
@ -613,7 +628,10 @@ mod tests {
// Verify shift operation occurred // Verify shift operation occurred
let (high, low) = sr.get_shift_register(); let (high, low) = sr.get_shift_register();
println!("DEBUG: After shift operation - high=0x{:x}, low=0x{:x}", high, low); println!(
"DEBUG: After shift operation - high=0x{:x}, low=0x{:x}",
high, low
);
println!("DEBUG: Shift register contents: {:?}", sr.shift_register); println!("DEBUG: Shift register contents: {:?}", sr.shift_register);
// The shift register should now contain the shifted data // The shift register should now contain the shifted data

View file

@ -3,7 +3,7 @@ pub mod component;
pub mod components; pub mod components;
pub mod connection; pub mod connection;
pub mod pin; pub mod pin;
pub mod systems; pub mod system_config;
pub mod types; pub mod types;
// Re-export commonly used items for easier importing // Re-export commonly used items for easier importing

View file

@ -1,4 +1,4 @@
use rusty_emu::systems::intel_mcs_4::IntelMcs4; use rusty_emu::system_config::{SystemFactory, ConfigurableSystem};
use std::env; use std::env;
use std::fs; use std::fs;
use std::process; use std::process;
@ -8,7 +8,7 @@ use std::time::{Duration, Instant};
fn main() { fn main() {
// Parse command line arguments // Parse command line arguments
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let mut system_type = "mcs4".to_string(); let mut system_type = "basic".to_string();
let mut program_file = "fibonacci.bin".to_string(); let mut program_file = "fibonacci.bin".to_string();
let mut i = 1; let mut i = 1;
@ -89,28 +89,46 @@ fn print_usage(program_name: &str) {
println!("Usage: {} [OPTIONS]", program_name); println!("Usage: {} [OPTIONS]", program_name);
println!(); println!();
println!("Options:"); println!("Options:");
println!(" -s, --system <SYSTEM> System type to run (default: mcs4)"); println!(" -s, --system <SYSTEM> System type to run (default: basic)");
println!(" Available: basic, max, fig1, or JSON config file");
println!(" -f, --file <FILE> Program binary file to load (default: fibonacci.bin)"); println!(" -f, --file <FILE> Program binary file to load (default: fibonacci.bin)");
println!(" -h, --help Show this help message"); println!(" -h, --help Show this help message");
println!(); println!();
println!("System Types:");
println!(" basic - Basic MCS-4 system (CPU, clock, 2 ROMs, 1 RAM)");
println!(" max - Fig.1 MCS-4 Max system (16 ROMs, 16 RAMs, shift registers)");
println!(" fig1 - Same as 'max'");
println!(" *.json - Custom system configuration file");
println!();
println!("Examples:"); println!("Examples:");
println!(" {} -s mcs4 -f fibonacci.bin", program_name); println!(" {} -s basic -f fibonacci.bin", program_name);
println!(" {} --system mcs4 --file myprogram.bin", program_name); println!(" {} --system max", program_name);
println!(" {} --system custom_config.json", program_name);
} }
fn load_program_data(filename: &str) -> Result<Vec<u8>, String> { fn load_program_data(filename: &str) -> Result<Vec<u8>, String> {
println!("DEBUG: Attempting to load program from: {}", filename); println!("DEBUG: Attempting to load program from: {}", filename);
match fs::read(filename) { match fs::read(filename) {
Ok(data) => { Ok(data) => {
println!("DEBUG: Successfully loaded {} bytes from {}", data.len(), filename); println!(
"DEBUG: Successfully loaded {} bytes from {}",
data.len(),
filename
);
Ok(data) Ok(data)
} }
Err(e) => { Err(e) => {
// If file doesn't exist, try to use default program // If file doesn't exist, try to use default program
if filename == "fibonacci.bin" { if filename == "fibonacci.bin" {
println!("DEBUG: File {} not found ({}), using default fibonacci program", filename, e); println!(
"DEBUG: File {} not found ({}), using default fibonacci program",
filename, e
);
let default_program = get_default_fibonacci_program(); let default_program = get_default_fibonacci_program();
println!("DEBUG: Default program size: {} bytes", default_program.len()); println!(
"DEBUG: Default program size: {} bytes",
default_program.len()
);
Ok(default_program) Ok(default_program)
} else { } else {
println!("DEBUG: Failed to read file {}: {}", filename, e); println!("DEBUG: Failed to read file {}: {}", filename, e);
@ -130,73 +148,61 @@ fn get_default_fibonacci_program() -> Vec<u8> {
0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number 0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number
0x10, 0x01, // LD 1 (Load accumulator from register 1) 0x10, 0x01, // LD 1 (Load accumulator from register 1)
0x50, // WRM (Write accumulator to RAM) 0x50, // WRM (Write accumulator to RAM)
// Fibonacci calculation loop // Fibonacci calculation loop
0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter 0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter
0x76, // IAC (Increment accumulator) 0x76, // IAC (Increment accumulator)
0x00, 0x02, // LD 2 (Store back to register 2) 0x00, 0x02, // LD 2 (Store back to register 2)
// Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2) // Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2)
0x00, 0x00, // LD 0 (Load F(n-2) into accumulator) 0x00, 0x00, // LD 0 (Load F(n-2) into accumulator)
0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator) 0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator)
0x50, // WRM (Store result to RAM) 0x50, // WRM (Store result to RAM)
// Update registers for next iteration // Update registers for next iteration
0x00, 0x01, // LD 1 (Load F(n-1) into accumulator) 0x00, 0x01, // LD 1 (Load F(n-1) into accumulator)
0x00, 0x00, // LD 0 (Store F(n-1) to register 0) 0x00, 0x00, // LD 0 (Store F(n-1) to register 0)
0x50, // WRM (Write to RAM - this will be read back as F(n-2)) 0x50, // WRM (Write to RAM - this will be read back as F(n-2))
// Check if we've calculated enough numbers (8 iterations) // Check if we've calculated enough numbers (8 iterations)
0x00, 0x02, // LD 2 (Load loop counter) 0x00, 0x02, // LD 2 (Load loop counter)
0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition 0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition
0x40, 0x0C, // JCN 12 (Jump back to loop start) 0x40, 0x0C, // JCN 12 (Jump back to loop start)
// Exit - halt program // Exit - halt program
0x00, 0x00, // NOP (placeholder for halt) 0x00, 0x00, // NOP (placeholder for halt)
] ]
} }
fn create_system(system_type: &str, program_data: &[u8]) -> Result<IntelMcs4, String> { fn create_system(system_type: &str, _program_data: &[u8]) -> Result<ConfigurableSystem, String> {
let factory = SystemFactory::new();
match system_type { match system_type {
"mcs4" => { "mcs4" | "basic" => {
let system = IntelMcs4::new(); // Use the basic MCS-4 configuration
factory.create_from_json("configs/mcs4_basic.json")
// Split program between ROMs .map_err(|e| format!("Failed to create basic MCS-4 system: {}", e))
let rom1_size = 256; }
let rom1_data = if program_data.len() > rom1_size { "mcs4_max" | "max" | "fig1" => {
program_data[..rom1_size].to_vec() // Use the Fig.1 MCS-4 Max configuration
factory.create_from_json("configs/mcs4_max.json")
.map_err(|e| format!("Failed to create MCS-4 Max system: {}", e))
}
_ => {
// Try to use provided config file directly
if system_type.ends_with(".json") {
factory.create_from_json(system_type)
.map_err(|e| format!("Failed to create system from '{}': {}", system_type, e))
} else { } else {
program_data.to_vec() Err(format!("Unknown system type: {}. Use 'basic', 'max', or provide a JSON config file path.", system_type))
}; }
let rom2_data = if program_data.len() > rom1_size {
program_data[rom1_size..].to_vec()
} else {
vec![0; 256]
};
// Load program into ROMs
let mut system = system;
system.load_program(rom1_data, rom2_data)
.map_err(|e| format!("Failed to load program: {}", e))?;
// Initialize RAM with some data for the program
let initial_data = [0x00, 0x01];
system.load_ram_data(&initial_data, 0)
.map_err(|e| format!("Failed to initialize RAM: {}", e))?;
system.reset_system();
Ok(system)
} }
_ => Err(format!("Unknown system type: {}", system_type))
} }
} }
fn run_system_demo(system: IntelMcs4) { fn run_system_demo(system: ConfigurableSystem) {
let start_time = Instant::now(); let start_time = Instant::now();
println!("Cycle | Accumulator | PC"); // Display system information
println!("------|-------------|------"); let info = system.get_system_info();
println!("System: {} - {}", info.name, info.description);
println!("Components: {}", info.component_count);
println!("CPU Speed: {} Hz", info.cpu_speed);
// Run system in a separate thread // Run system in a separate thread
let system_arc = std::sync::Arc::new(std::sync::Mutex::new(system)); let system_arc = std::sync::Arc::new(std::sync::Mutex::new(system));
@ -208,25 +214,20 @@ fn run_system_demo(system: IntelMcs4) {
} }
}); });
// Monitor CPU state in main thread // Monitor system state
let mut iteration = 0; let mut iteration = 0;
while iteration < 10 {
while iteration < 20 { thread::sleep(Duration::from_millis(500));
thread::sleep(Duration::from_millis(200));
if let Ok(system) = system_arc.lock() { if let Ok(system) = system_arc.lock() {
if let Ok(state) = system.get_cpu_state() { if !system.is_running() {
println!( break;
"{:5} | {:11} | {:04X}",
state.cycle_count,
state.accumulator,
state.program_counter
);
iteration += 1;
} }
} }
if iteration >= 15 { iteration += 1;
if iteration >= 8 {
if let Ok(mut system) = system_arc.lock() { if let Ok(mut system) = system_arc.lock() {
system.stop(); system.stop();
} }

351
src/system_config.rs Normal file
View file

@ -0,0 +1,351 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::component::Component;
use std::sync::{Arc, Mutex};
/// JSON-based system configuration structures
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemConfig {
pub name: String,
pub description: String,
pub version: String,
pub metadata: HashMap<String, serde_json::Value>,
pub components: HashMap<String, ComponentConfig>,
pub connections: HashMap<String, ConnectionConfig>,
pub layout: Option<LayoutConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComponentConfig {
#[serde(rename = "single")]
Single(SingleComponentConfig),
#[serde(rename = "array")]
Array(ArrayComponentConfig),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SingleComponentConfig {
pub component_type: String,
pub name: String,
pub properties: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArrayComponentConfig {
pub component_type: String,
pub count: usize,
pub naming_pattern: String,
pub properties: HashMap<String, serde_json::Value>,
pub overrides: Option<HashMap<String, HashMap<String, serde_json::Value>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionConfig {
pub connection_type: String,
pub source: PinReference,
pub targets: Vec<PinReference>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PinReference {
pub component: String,
pub pin: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutConfig {
pub grid_size: [usize; 2],
pub positions: HashMap<String, [usize; 2]>,
}
/// System factory for creating systems from JSON configuration
pub struct SystemFactory {
component_registry: HashMap<String, fn(config: &ComponentConfig, name: String) -> Result<Box<dyn Component>, String>>,
}
impl SystemFactory {
pub fn new() -> Self {
let mut factory = SystemFactory {
component_registry: HashMap::new(),
};
factory.register_default_components();
factory
}
fn register_default_components(&mut self) {
// Register component creation functions
self.component_registry.insert(
"intel_4004".to_string(),
|config: &ComponentConfig, name: String| {
if let ComponentConfig::Single(single) = config {
let clock_speed = single.properties.get("clock_speed")
.and_then(|v| v.as_f64())
.unwrap_or(750000.0);
Ok(Box::new(crate::components::cpu::intel_4004::Intel4004::new(name, clock_speed)))
} else {
Err("Intel 4004 must be single component".to_string())
}
}
);
self.component_registry.insert(
"generic_clock".to_string(),
|config: &ComponentConfig, name: String| {
if let ComponentConfig::Single(single) = config {
let frequency = single.properties.get("frequency")
.and_then(|v| v.as_f64())
.unwrap_or(750000.0);
Ok(Box::new(crate::components::clock::generic_clock::GenericClock::new(name, frequency)))
} else {
Err("Generic clock must be single component".to_string())
}
}
);
self.component_registry.insert(
"intel_4001".to_string(),
|config: &ComponentConfig, name: String| {
if let ComponentConfig::Single(_single) = config {
Ok(Box::new(crate::components::memory::intel_4001::Intel4001::new(name)))
} else {
Err("Intel 4001 must be single component".to_string())
}
}
);
self.component_registry.insert(
"intel_4002".to_string(),
|config: &ComponentConfig, name: String| {
if let ComponentConfig::Single(single) = config {
let variant = single.properties.get("variant")
.and_then(|v| v.as_str())
.unwrap_or("Type1");
let access_time = single.properties.get("access_time")
.and_then(|v| v.as_u64())
.unwrap_or(500);
let ram_variant = match variant {
"Type2" => crate::components::memory::intel_4002::RamVariant::Type2,
_ => crate::components::memory::intel_4002::RamVariant::Type1,
};
Ok(Box::new(crate::components::memory::intel_4002::Intel4002::new_with_variant_and_access_time(
name, ram_variant, access_time
)))
} else {
Err("Intel 4002 must be single component".to_string())
}
}
);
self.component_registry.insert(
"intel_4003".to_string(),
|config: &ComponentConfig, name: String| {
if let ComponentConfig::Single(_single) = config {
Ok(Box::new(crate::components::memory::intel_4003::Intel4003::new(name)))
} else {
Err("Intel 4003 must be single component".to_string())
}
}
);
}
pub fn create_from_json(&self, json_path: &str) -> Result<ConfigurableSystem, String> {
let config: SystemConfig = self.load_json_config(json_path)?;
let mut components = self.create_components(&config)?;
self.connect_components(&config, &mut components)?;
Ok(ConfigurableSystem::new(config, components))
}
fn load_json_config(&self, path: &str) -> Result<SystemConfig, String> {
let content = std::fs::read_to_string(path)
.map_err(|e| format!("Failed to read config file '{}': {}", path, e))?;
serde_json::from_str(&content)
.map_err(|e| format!("Failed to parse JSON config '{}': {}", path, e))
}
fn create_components(&self, config: &SystemConfig) -> Result<HashMap<String, Arc<Mutex<Box<dyn Component>>>>, String> {
let mut components = HashMap::new();
for (id, component_config) in &config.components {
let component_names = self.expand_component_names(id, component_config);
for component_name in component_names {
let component = self.create_single_component(component_config, component_name.clone())?;
components.insert(component_name, Arc::new(Mutex::new(component)));
}
}
Ok(components)
}
fn expand_component_names(&self, _id: &str, config: &ComponentConfig) -> Vec<String> {
match config {
ComponentConfig::Single(single) => vec![single.name.clone()],
ComponentConfig::Array(array) => {
let mut names = Vec::new();
for i in 0..array.count {
let name = array.naming_pattern.replace("{:02}", &format!("{:02}", i));
names.push(name);
}
names
}
}
}
fn create_single_component(&self, config: &ComponentConfig, name: String) -> Result<Box<dyn Component>, String> {
match config {
ComponentConfig::Single(single) => {
if let Some(creator) = self.component_registry.get(&single.component_type) {
creator(config, name)
} else {
Err(format!("Unknown component type: {}", single.component_type))
}
}
ComponentConfig::Array(array) => {
if let Some(creator) = self.component_registry.get(&array.component_type) {
creator(config, name)
} else {
Err(format!("Unknown component type: {}", array.component_type))
}
}
}
}
fn connect_components(&self, config: &SystemConfig, components: &mut HashMap<String, Arc<Mutex<Box<dyn Component>>>>) -> Result<(), String> {
for (connection_id, connection_config) in &config.connections {
println!("Connecting: {}", connection_id);
// Get source pin
let source_component = components.get(&connection_config.source.component)
.ok_or_else(|| format!("Source component not found: {}", connection_config.source.component))?;
let source_pin = {
let component = source_component.lock().unwrap();
component.get_pin(&connection_config.source.pin)
.map_err(|e| format!("Failed to get source pin: {}", e))?
};
// Connect to all targets
for target_ref in &connection_config.targets {
let target_component = components.get(&target_ref.component)
.ok_or_else(|| format!("Target component not found: {}", target_ref.component))?;
let target_pin = {
let component = target_component.lock().unwrap();
component.get_pin(&target_ref.pin)
.map_err(|e| format!("Failed to get target pin: {}", e))?
};
// Connect the pins
let _source_pin_guard = source_pin.lock().unwrap();
let mut target_pin_guard = target_pin.lock().unwrap();
target_pin_guard.connect_to(source_pin.clone());
}
}
Ok(())
}
}
/// A configurable system created from JSON configuration
pub struct ConfigurableSystem {
config: SystemConfig,
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
is_running: bool,
}
impl ConfigurableSystem {
pub fn new(config: SystemConfig, components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>) -> Self {
ConfigurableSystem {
config,
components,
is_running: false,
}
}
pub fn run(&mut self) {
self.is_running = true;
let mut handles = vec![];
println!("Starting configurable system: {}", self.config.name);
println!("Description: {}", self.config.description);
for (name, component) in &self.components {
let comp_clone = Arc::clone(component);
let name_clone = name.clone();
let handle = std::thread::spawn(move || {
println!("Starting component: {}", name_clone);
if let Ok(mut comp) = comp_clone.lock() {
comp.run();
}
println!("Component {} stopped", name_clone);
});
handles.push((name.clone(), handle));
}
println!("All components started. System running...");
std::thread::sleep(std::time::Duration::from_secs(2));
self.is_running = false;
// Stop all components
println!("\nStopping system components...");
for (name, component) in &self.components {
if let Ok(mut comp) = component.lock() {
comp.stop();
println!("Stopped component: {}", name);
}
}
// Wait for threads
for (name, handle) in handles {
match handle.join() {
Ok(_) => println!("Component {} thread finished", name),
Err(_) => eprintln!("Component {} thread panicked", name),
}
}
println!("Configurable system stopped.");
}
pub fn stop(&mut self) {
self.is_running = false;
}
pub fn is_running(&self) -> bool {
self.is_running
}
pub fn get_system_info(&self) -> SystemInfo {
let rom_size = self.config.metadata.get("rom_size")
.and_then(|v| v.as_u64())
.unwrap_or(256) as usize;
let ram_size = self.config.metadata.get("ram_size")
.and_then(|v| v.as_u64())
.unwrap_or(40) as usize;
SystemInfo {
name: self.config.name.clone(),
description: self.config.description.clone(),
component_count: self.components.len(),
cpu_speed: self.config.metadata.get("cpu_speed")
.and_then(|v| v.as_f64())
.unwrap_or(750000.0),
rom_size,
ram_size,
}
}
}
#[derive(Debug, Clone)]
pub struct SystemInfo {
pub name: String,
pub description: String,
pub component_count: usize,
pub cpu_speed: f64,
pub rom_size: usize,
pub ram_size: usize,
}

View file

@ -1,362 +0,0 @@
# System Integration
This directory contains complete system implementations that integrate multiple components into functional microprocessor systems.
## System Architecture
Systems represent complete working computers built from individual components. They handle:
- **Component Interconnection**: Connecting pins between components
- **Clock Distribution**: Providing synchronized clocks to all components
- **Bus Management**: Coordinating access to shared buses
- **System Lifecycle**: Initialization, execution, and shutdown
- **Program Loading**: Loading and executing software
## Current Systems
### Intel MCS-4 System
#### Overview
The Intel MCS-4 (Micro Computer System) was Intel's first microprocessor system, consisting of:
- **Intel 4004 CPU**: 4-bit central processing unit
- **Intel 4001 ROM**: Program storage with I/O ports
- **Intel 4002 RAM**: Data storage with output ports
- **Intel 4003 Shift Register**: Serial I/O interface
#### Architecture
```
┌─────────────────┐ ┌─────────────────┐
│ Intel 4004 │ │ Intel 4001 │
│ CPU │◄──►│ ROM │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Intel 4002 │ │ Intel 4003 │
│ RAM │ │ Shift Register │
└─────────────────┘ └─────────────────┘
```
#### System Components
**CPU Subsystem**
- Intel 4004 processor with full instruction set
- Program counter and stack management
- Register file (16 × 4-bit registers)
- ALU with carry/borrow logic
**Memory Subsystem**
- ROM units for program storage
- RAM units for data storage
- Memory-mapped I/O ports
- Banked memory organization
**I/O Subsystem**
- Shift registers for serial communication
- Output ports for status display
- Input ports for external data
#### Clock System
The MCS-4 uses a two-phase clock system:
- **Φ1 (Phase 1)**: CPU drives address bus, memory latches addresses
- **Φ2 (Phase 2)**: Memory drives data bus, CPU reads data
Clock timing is critical for proper system operation:
```
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │ │ │
│ Φ1 │ │ Φ2 │ │ Φ1 │ │ Φ2 │
│ │ │ │ │ │ │ │
└─────┘ └─────┘ └─────┘ └─────┘
T1 T2 T3 T4
```
#### Memory Organization
**Address Space**
- **12-bit address bus** (4096 possible locations)
- **4-bit data bus** (nibbles)
- **8-bit instructions** (2 nibbles each)
- **16-bit addresses** (4 nibbles each)
**Memory Map**
- ROM and RAM share the same address space
- Chip select signals determine which memory responds
- I/O ports are memory-mapped
#### Implementation Status
**Currently Implemented**
- ✅ Basic system structure and component interconnection
- ✅ Clock generation and distribution
- ✅ Memory subsystem with ROM and RAM
- ✅ CPU integration with memory
- ✅ Basic I/O port functionality
**Partially Implemented**
- ⚠️ Complete instruction execution pipeline
- ⚠️ Interrupt handling system
- ⚠️ DMA (Direct Memory Access) support
- ⚠️ Advanced I/O device support
**Not Yet Implemented**
- ❌ Complex peripheral devices
- ❌ Operating system integration
- ❌ Development tools integration
- ❌ Performance optimization
## System Integration Patterns
### Component Connection
Components connect through shared pin references:
```rust
// Create system components
let cpu = Intel4004::new("CPU".to_string());
let rom = Intel4001::new("ROM".to_string());
// Connect data bus pins
let cpu_data_pin = cpu.get_pin("D0")?;
let rom_data_pin = rom.get_pin("D0")?;
connect_pins(cpu_data_pin, rom_data_pin)?;
// Connect clock signals
let cpu_clock_pin = cpu.get_pin("PHI1")?;
let rom_clock_pin = rom.get_pin("PHI1")?;
connect_pins(cpu_clock_pin, rom_clock_pin)?;
```
### Clock Synchronization
All components receive synchronized clock signals:
```rust
// Create clock generator
let clock = GenericClock::new("SystemClock".to_string());
// Distribute clock to all components
let clock_output = clock.get_pin("OUT")?;
cpu.connect_clock(clock_output.clone())?;
rom.connect_clock(clock_output.clone())?;
ram.connect_clock(clock_output)?;
```
### Bus Arbitration
Multiple components may drive the same bus:
```rust
// CPU drives bus during Φ1
cpu.set_bus_driver(true)?;
// Memory drives bus during Φ2
rom.set_bus_driver(true)?;
ram.set_bus_driver(true)?;
// System manages arbitration
system.arbitrate_bus_access()?;
```
## Usage Examples
### Basic System Setup
```rust
use rusty_emu::systems::intel_mcs_4::IntelMCS4;
// Create complete MCS-4 system
let mut system = IntelMCS4::new();
// Load program into ROM
let program = vec![
0x12, 0x34, // Sample instructions
0x56, 0x78,
// ... more program data
];
system.load_program(0, &program)?;
// Run the system
system.run();
```
### System Configuration
```rust
// Configure system parameters
system.set_clock_frequency(750_000.0)?; // 750 kHz
system.set_memory_size(1024)?; // 1KB total memory
system.enable_debug_mode(true)?; // Enable detailed logging
// Add peripheral devices
system.add_peripheral("Terminal".to_string(), terminal_device)?;
system.add_peripheral("Storage".to_string(), storage_device)?;
```
### Program Development
```rust
// Load and debug programs
system.load_program_from_file("program.bin")?;
// Set breakpoints
system.set_breakpoint(0x100)?;
// Run with debugging
system.run_with_debug()?;
// Inspect system state
println!("PC: 0x{:04X}", system.get_program_counter()?);
println!("ACC: 0x{:02X}", system.get_accumulator()?);
```
## Testing Strategy
### System-Level Tests
System integration requires comprehensive testing:
1. **Component Interaction Tests**
- Verify CPU can read from ROM
- Verify CPU can write to RAM
- Verify I/O operations work correctly
2. **Timing Verification Tests**
- Verify clock synchronization
- Verify bus timing constraints
- Verify memory access timing
3. **Program Execution Tests**
- Test simple programs execute correctly
- Test I/O operations
- Test error conditions
### Debugging Support
The system provides debugging capabilities:
```rust
// Enable debug tracing
system.enable_tracing(true)?;
// Set up debug output
system.set_debug_output_file("debug.log")?;
// Monitor specific signals
system.monitor_pin("CPU", "D0")?;
system.monitor_pin("ROM", "DATA_READY")?;
```
## Performance Considerations
### Simulation Performance
System simulation performance depends on:
- **Component Count**: More components = slower simulation
- **Clock Frequency**: Higher frequency = more updates per second
- **Debug Output**: Logging reduces performance
- **Pin Monitoring**: Signal monitoring adds overhead
### Optimization Strategies
1. **Selective Updates**: Only update components that need attention
2. **Event-Driven Simulation**: Respond to events rather than polling
3. **Lazy Evaluation**: Delay expensive operations until needed
4. **Caching**: Cache frequently accessed state
## Hardware Accuracy
### MCS-4 Specifics
The implementation aims for hardware accuracy:
- **Cycle-Accurate Timing**: Where possible given Rust limitations
- **Proper Signal Levels**: Correct High/Low/HighZ states
- **Bus Contention Handling**: Proper arbitration and error detection
- **Clock Phase Relationships**: Correct Φ1/Φ2 timing
### Known Limitations
- **Timing Precision**: Limited by OS scheduling and Rust timing
- **Parallel Execution**: Hardware runs components in parallel, simulation is sequential
- **Signal Propagation**: Simplified delay modeling
- **Power Simulation**: No power consumption modeling
## Future Enhancements
### Planned Features
1. **Enhanced I/O Support**
- Serial communication interfaces
- Parallel I/O expansion
- Analog-to-digital conversion
- Real-time clock support
2. **Development Tools Integration**
- Assembler integration
- Debugger interface
- Program loading and saving
- Memory inspection tools
3. **Performance Improvements**
- Multi-threaded component execution
- Optimized timing simulation
- Memory layout optimization
- Caching for frequently accessed data
4. **Extended System Support**
- MCS-40 system support
- Enhanced peripheral set
- Networking capabilities
- Storage device emulation
### Extension Points
The system architecture is designed for extensibility:
- **New Component Types**: Easy addition of new hardware
- **System Variants**: Support for different MCS-4 configurations
- **Custom Peripherals**: Integration of specialized devices
- **Alternative Architectures**: Framework for other processor families
## Troubleshooting
### Common Issues
1. **Clock Synchronization Problems**
- Verify all components receive the same clock signal
- Check clock phase relationships
- Ensure proper setup/hold time compliance
2. **Bus Contention Issues**
- Verify only one component drives the bus at a time
- Check tri-state timing
- Monitor for conflicting drivers
3. **Memory Access Problems**
- Verify address decoding is correct
- Check chip select signal timing
- Ensure proper memory bank selection
### Debug Tools
The system provides debugging utilities:
```rust
// Enable verbose logging
system.set_log_level(LogLevel::Debug)?;
// Monitor all pin states
system.monitor_all_pins()?;
// Generate timing diagrams
system.generate_timing_diagram("timing.txt")?;
```
This system integration provides a solid foundation for building complete microprocessor systems with proper component interaction, timing, and debugging support.

View file

@ -1,306 +0,0 @@
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use crate::component::Component;
use crate::components::cpu::intel_4004::Intel4004;
use crate::pin::Pin;
pub struct IntelMcs4 {
components: HashMap<String, Arc<Mutex<dyn Component>>>,
is_running: bool,
}
impl IntelMcs4 {
pub fn new() -> Self {
let system = IntelMcs4 {
components: HashMap::new(),
is_running: false,
};
system
}
pub fn run(&mut self) {
self.is_running = true;
let mut handles = vec![];
println!("Starting MCS-4 system components...");
for (name, component) in &self.components {
let comp_clone = Arc::clone(component);
let name_clone = name.clone();
let handle = thread::spawn(move || {
println!("Starting component: {}", name_clone);
if let Ok(mut comp) = comp_clone.lock() {
comp.run();
}
println!("Component {} stopped", name_clone);
});
handles.push((name.clone(), handle));
}
println!("All components started. System running...");
println!("CPU will execute loaded program...");
// Monitor system and display system status
let mut last_cycle_count = 0;
let mut display_counter = 0;
while self.is_running {
thread::sleep(Duration::from_millis(50));
// Get current CPU state
if let Ok(cpu_state) = self.get_cpu_state() {
// Display system status periodically
if cpu_state.cycle_count - last_cycle_count > 100 {
self.display_system_status();
last_cycle_count = cpu_state.cycle_count;
display_counter += 1;
}
// Run for a reasonable amount of time to see the calculation
if display_counter > 20 {
self.is_running = false;
}
}
}
// Stop all components
println!("\nStopping system components...");
for (name, component) in &self.components {
if let Ok(mut comp) = component.lock() {
comp.stop();
println!("Stopped component: {}", name);
}
}
// Wait for threads
for (name, handle) in handles {
match handle.join() {
Ok(_) => println!("Component {} thread finished", name),
Err(_) => eprintln!("Component {} thread panicked", name),
}
}
println!("MCS-4 system stopped.");
println!("\nFinal system status:");
self.display_system_status();
}
/// Display the current system status
fn display_system_status(&self) {
println!("System execution in progress...");
if let Ok(cpu_state) = self.get_cpu_state() {
println!("CPU State - PC: 0x{:03X}, ACC: 0x{:X}, Cycles: {}",
cpu_state.program_counter, cpu_state.accumulator, cpu_state.cycle_count);
}
}
pub fn stop(&mut self) {
self.is_running = false;
}
pub fn is_running(&self) -> bool {
self.is_running
}
// Implement the missing methods for main.rs
pub fn load_program(&mut self, rom1_data: Vec<u8>, rom2_data: Vec<u8>) -> Result<(), String> {
println!("Loading program into ROM...");
println!("ROM1 data: {} bytes", rom1_data.len());
println!("ROM2 data: {} bytes", rom2_data.len());
Ok(())
}
pub fn load_rom_data(
&mut self,
rom_chip: usize,
data: Vec<u8>,
offset: usize,
) -> Result<(), String> {
let rom_key = match rom_chip {
1 => "rom1",
2 => "rom2",
_ => return Err("Invalid ROM chip".to_string()),
};
if let Some(rom_component) = self.components.get(rom_key) {
if let Ok(_rom) = rom_component.lock() {
// For now, just log the operation
println!(
"Loaded {} bytes into ROM{} at offset {}",
data.len(),
rom_chip,
offset
);
return Ok(());
}
}
Err("ROM component not found".to_string())
}
pub fn load_ram_data(&mut self, data: &[u8], offset: usize) -> Result<(), String> {
if let Some(ram_component) = self.components.get("ram") {
if let Ok(_ram) = ram_component.lock() {
println!("Loaded {} bytes into RAM at offset {}", data.len(), offset);
return Ok(());
}
}
Err("RAM component not found".to_string())
}
pub fn get_cpu_state(&self) -> Result<CpuState, String> {
if let Some(cpu_component) = self.components.get("cpu") {
if let Some(cpu) = cpu_component.as_any().downcast_ref::<Intel4004>() {
return Ok(CpuState {
program_counter: cpu.get_program_counter(),
accumulator: cpu.get_accumulator(),
carry: cpu.get_carry(),
stack_pointer: cpu.get_stack_pointer(),
cycle_count: cpu.get_cycle_count(),
});
} else {
Err("CPU component is not of type Intel 4004".to_string())
}
} else {
Err("CPU component not found".to_string())
}
}
pub fn reset_system(&mut self) {
println!("Resetting MCS-4 system...");
if let Some(cpu_component) = self.components.get_mut("cpu") {
if let Some(cpu) = cpu_component.as_any_mut().downcast_mut::<Intel4004>() {
cpu.reset();
}
}
}
pub fn get_system_info(&self) -> SystemInfo {
SystemInfo {
cpu_speed: 750_000.0,
rom_size: 512,
ram_size: 40,
component_count: self.components.len(),
}
}
pub fn get_connection_graph(&self) -> Vec<Vec<String>> {
vec![
vec!["clock".to_string(), "cpu".to_string()],
vec!["clock".to_string(), "ram".to_string()],
vec!["clock".to_string(), "rom1".to_string()],
vec!["clock".to_string(), "rom2".to_string()],
vec!["clock".to_string(), "shift_reg".to_string()],
vec!["cpu".to_string(), "ram".to_string()],
vec!["cpu".to_string(), "rom1".to_string()],
vec!["cpu".to_string(), "rom2".to_string()],
vec!["cpu".to_string(), "shift_reg".to_string()],
]
}
/// Get detailed system status including all component states
pub fn get_detailed_status(&self) -> SystemStatus {
let cpu_state = self.get_cpu_state().ok();
let system_info = self.get_system_info();
SystemStatus {
is_running: self.is_running,
cpu_state,
system_info,
components_status: self.get_components_status(),
}
}
/// Get status of all components
fn get_components_status(&self) -> HashMap<String, String> {
let mut status = HashMap::new();
for (name, component) in &self.components {
if let Ok(comp) = component.lock() {
status.insert(name.clone(), format!("Running: {}", comp.is_running()));
} else {
status.insert(name.clone(), "Locked".to_string());
}
}
status
}
}
// Add the missing AsAny trait implementation
trait AsAny {
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}
impl<T: 'static> AsAny for T {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
#[derive(Debug, Clone)]
pub struct CpuState {
pub program_counter: u16,
pub accumulator: u8,
pub carry: bool,
pub stack_pointer: u8,
pub cycle_count: u64,
}
#[derive(Debug, Clone)]
pub struct SystemInfo {
pub cpu_speed: f64,
pub rom_size: usize,
pub ram_size: usize,
pub component_count: usize,
}
#[derive(Debug, Clone)]
pub struct SystemStatus {
pub is_running: bool,
pub cpu_state: Option<CpuState>,
pub system_info: SystemInfo,
pub components_status: HashMap<String, String>,
}
// Implement Component for IntelMcs4 for completeness
impl Component for IntelMcs4 {
fn name(&self) -> String {
"Intel_MCS-4_System".to_string()
}
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
HashMap::new()
}
fn get_pin(&self, _name: &str) -> Result<Arc<Mutex<Pin>>, String> {
Err("System pins not accessible".to_string())
}
fn update(&mut self) {
// System-level update
}
fn run(&mut self) {
self.run();
}
fn stop(&mut self) {
self.stop();
}
fn is_running(&self) -> bool {
self.is_running()
}
}

View file

@ -1,445 +0,0 @@
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::component::Component;
use crate::components::clock::generic_clock::GenericClock;
use crate::components::cpu::intel_4004::Intel4004;
use crate::components::memory::intel_4001::Intel4001;
use crate::components::memory::intel_4002::{Intel4002, RamVariant};
use crate::components::memory::intel_4003::Intel4003;
use crate::pin::Pin;
/// Intel MCS-4 System implementation according to Fig.1 configuration
/// Features 16 ROMs and 16 RAMs with specific connectivity requirements
pub struct IntelMcs4Max {
components: HashMap<String, Arc<Mutex<dyn Component>>>,
is_running: bool,
// Fig.1 configuration: 16 ROMs and 16 RAMs
rom_chips: Vec<Arc<Mutex<Intel4001>>>,
ram_chips: Vec<Arc<Mutex<Intel4002>>>,
shift_registers: Vec<Arc<Mutex<Intel4003>>>,
}
impl IntelMcs4Max {
pub fn new() -> Self {
let mut system = IntelMcs4Max {
components: HashMap::new(),
is_running: false,
rom_chips: Vec::new(),
ram_chips: Vec::new(),
shift_registers: Vec::new(),
};
system.initialize_fig1_system();
system
}
/// Initialize the MCS-4 system according to Fig.1 configuration
/// 16 ROMs and 16 RAMs with specific connectivity
fn initialize_fig1_system(&mut self) {
// Create CPU with proper clock speed
let cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0);
self.components.insert("cpu".to_string(), Arc::new(Mutex::new(cpu)));
// Create clock generator
let clock = GenericClock::new("SYSTEM_CLOCK".to_string(), 750_000.0);
self.components.insert("clock".to_string(), Arc::new(Mutex::new(clock)));
// Create 16 ROM chips (4001)
for i in 0..16 {
let rom_name = format!("ROM_4001_{:02}", i);
let rom = Intel4001::new(rom_name.clone());
let rom_arc = Arc::new(Mutex::new(rom));
self.components.insert(format!("rom_{:02}", i), rom_arc.clone());
self.rom_chips.push(rom_arc);
}
// Create 16 RAM chips (4002) with variants
for i in 0..16 {
let ram_name = format!("RAM_4002_{:02}", i);
// Use 4002-1 variant for most chips, 4002-2 for specific ones as per requirements
let variant = if i == 3 { RamVariant::Type2 } else { RamVariant::Type1 };
let ram = Intel4002::new_with_variant_and_access_time(ram_name.clone(), variant, 500);
let ram_arc = Arc::new(Mutex::new(ram));
self.components.insert(format!("ram_{:02}", i), ram_arc.clone());
self.ram_chips.push(ram_arc);
}
// Create shift registers (4003) for serial connections
// ROM 15 feeds a 4003
let shift_reg1 = Intel4003::new("SHIFT_4003_ROM15".to_string());
let shift_reg1_arc = Arc::new(Mutex::new(shift_reg1));
self.components.insert("shift_reg_rom15".to_string(), shift_reg1_arc.clone());
self.shift_registers.push(shift_reg1_arc);
// 4002-1 on CM-RAM3 line feeds series of two 4003 chips
let shift_reg2 = Intel4003::new("SHIFT_4003_RAM3_1".to_string());
let shift_reg2_arc = Arc::new(Mutex::new(shift_reg2));
self.components.insert("shift_reg_ram3_1".to_string(), shift_reg2_arc.clone());
self.shift_registers.push(shift_reg2_arc);
let shift_reg3 = Intel4003::new("SHIFT_4003_RAM3_2".to_string());
let shift_reg3_arc = Arc::new(Mutex::new(shift_reg3));
self.components.insert("shift_reg_ram3_2".to_string(), shift_reg3_arc.clone());
self.shift_registers.push(shift_reg3_arc);
// Connect components via pin connections
self.connect_fig1_components();
}
/// Connect components according to Fig.1 configuration
fn connect_fig1_components(&mut self) {
// Connect clock signals to all components
self.connect_clock_signals();
// Connect CPU control signals to memory components
self.connect_control_signals();
// Connect data bus between CPU and memory components
self.connect_data_bus();
// Connect specific Fig.1 requirements
self.connect_fig1_specific_connections();
}
/// Connect clock signals from clock generator to all components
fn connect_clock_signals(&mut self) {
let clock_phi1_pin = self.components.get("clock").unwrap().lock().unwrap().get_pin("CLK").unwrap();
let clock_phi2_pin = clock_phi1_pin.clone(); // Simplified for now
// Connect to CPU
if let Some(cpu_component) = self.components.get("cpu") {
if let Ok(cpu) = cpu_component.lock() {
let cpu_phi1 = cpu.get_pin("PHI1").unwrap();
let cpu_phi2 = cpu.get_pin("PHI2").unwrap();
if let Ok(_clock_phi1_guard) = clock_phi1_pin.lock() {
if let Ok(mut cpu_phi1_guard) = cpu_phi1.lock() {
cpu_phi1_guard.connect_to(clock_phi1_pin.clone());
}
}
if let Ok(_clock_phi2_guard) = clock_phi2_pin.lock() {
if let Ok(mut cpu_phi2_guard) = cpu_phi2.lock() {
cpu_phi2_guard.connect_to(clock_phi2_pin.clone());
}
}
}
}
// Connect to all ROMs
for rom_arc in &self.rom_chips {
if let Ok(mut rom) = rom_arc.lock() {
let rom_phi1 = rom.get_pin("PHI1").unwrap();
let rom_phi2 = rom.get_pin("PHI2").unwrap();
if let Ok(_clock_phi1_guard) = clock_phi1_pin.lock() {
if let Ok(mut rom_phi1_guard) = rom_phi1.lock() {
rom_phi1_guard.connect_to(clock_phi1_pin.clone());
}
}
if let Ok(_clock_phi2_guard) = clock_phi2_pin.lock() {
if let Ok(mut rom_phi2_guard) = rom_phi2.lock() {
rom_phi2_guard.connect_to(clock_phi2_pin.clone());
}
}
}
}
// Connect to all RAMs
for ram_arc in &self.ram_chips {
if let Ok(mut ram) = ram_arc.lock() {
let ram_phi1 = ram.get_pin("PHI1").unwrap();
let ram_phi2 = ram.get_pin("PHI2").unwrap();
if let Ok(_clock_phi1_guard) = clock_phi1_pin.lock() {
if let Ok(mut ram_phi1_guard) = ram_phi1.lock() {
ram_phi1_guard.connect_to(clock_phi1_pin.clone());
}
}
if let Ok(_clock_phi2_guard) = clock_phi2_pin.lock() {
if let Ok(mut ram_phi2_guard) = ram_phi2.lock() {
ram_phi2_guard.connect_to(clock_phi2_pin.clone());
}
}
}
}
// Connect to all shift registers
for sr_arc in &self.shift_registers {
if let Ok(mut sr) = sr_arc.lock() {
let sr_phi1 = sr.get_pin("PHI1").unwrap();
let sr_phi2 = sr.get_pin("PHI2").unwrap();
if let Ok(_clock_phi1_guard) = clock_phi1_pin.lock() {
if let Ok(mut sr_phi1_guard) = sr_phi1.lock() {
sr_phi1_guard.connect_to(clock_phi1_pin.clone());
}
}
if let Ok(_clock_phi2_guard) = clock_phi2_pin.lock() {
if let Ok(mut sr_phi2_guard) = sr_phi2.lock() {
sr_phi2_guard.connect_to(clock_phi2_pin.clone());
}
}
}
}
}
/// Connect control signals between CPU and memory components
fn connect_control_signals(&mut self) {
if let Some(cpu_component) = self.components.get("cpu") {
if let Ok(cpu) = cpu_component.lock() {
let cpu_sync = cpu.get_pin("SYNC").unwrap();
let cpu_cm_rom = cpu.get_pin("CM_ROM").unwrap();
let _cpu_cm_ram = cpu.get_pin("CM_RAM").unwrap();
// Connect to all ROMs
for (_i, rom_arc) in self.rom_chips.iter().enumerate() {
if let Ok(mut rom) = rom_arc.lock() {
let rom_sync = rom.get_pin("SYNC").unwrap();
let rom_cm = rom.get_pin("CM").unwrap();
let rom_ci = rom.get_pin("CI").unwrap();
if let Ok(_cpu_sync_guard) = cpu_sync.lock() {
if let Ok(mut rom_sync_guard) = rom_sync.lock() {
rom_sync_guard.connect_to(cpu_sync.clone());
}
}
if let Ok(_cpu_cm_rom_guard) = cpu_cm_rom.lock() {
if let Ok(mut rom_cm_guard) = rom_cm.lock() {
rom_cm_guard.connect_to(cpu_cm_rom.clone());
}
if let Ok(mut rom_ci_guard) = rom_ci.lock() {
rom_ci_guard.connect_to(cpu_cm_rom.clone());
}
}
}
}
// Connect to all RAMs
for (_i, ram_arc) in self.ram_chips.iter().enumerate() {
if let Ok(mut ram) = ram_arc.lock() {
let ram_sync = ram.get_pin("SYNC").unwrap();
let ram_p0 = ram.get_pin("P0").unwrap();
if let Ok(_cpu_sync_guard) = cpu_sync.lock() {
if let Ok(mut ram_sync_guard) = ram_sync.lock() {
ram_sync_guard.connect_to(cpu_sync.clone());
}
if let Ok(mut ram_p0_guard) = ram_p0.lock() {
ram_p0_guard.connect_to(cpu_sync.clone());
}
}
}
}
}
}
}
/// Connect data bus between CPU and memory components
fn connect_data_bus(&mut self) {
if let Some(cpu_component) = self.components.get("cpu") {
if let Ok(cpu) = cpu_component.lock() {
// Connect CPU data pins to all memory component data pins
for data_pin_idx in 0..4 {
let pin_name = format!("D{}", data_pin_idx);
let cpu_data_pin = cpu.get_pin(&pin_name).unwrap();
// Connect to all ROMs
for rom_arc in &self.rom_chips {
if let Ok(mut rom) = rom_arc.lock() {
let rom_data_pin = rom.get_pin(&pin_name).unwrap();
if let Ok(_cpu_pin_guard) = cpu_data_pin.lock() {
if let Ok(mut rom_pin_guard) = rom_data_pin.lock() {
rom_pin_guard.connect_to(cpu_data_pin.clone());
}
}
}
}
// Connect to all RAMs
for ram_arc in &self.ram_chips {
if let Ok(mut ram) = ram_arc.lock() {
let ram_data_pin = ram.get_pin(&pin_name).unwrap();
if let Ok(_cpu_pin_guard) = cpu_data_pin.lock() {
if let Ok(mut ram_pin_guard) = ram_data_pin.lock() {
ram_pin_guard.connect_to(cpu_data_pin.clone());
}
}
}
}
}
}
}
}
/// Connect Fig.1 specific connections
fn connect_fig1_specific_connections(&mut self) {
// ROM 15 feeds a 4003
if let Some(rom15) = self.rom_chips.get(15) {
if let Some(shift_reg) = self.shift_registers.get(0) {
// Connect ROM 15 I/O pins to shift register serial input
for i in 0..4 {
if let Ok(rom) = rom15.lock() {
if let Ok(sr) = shift_reg.lock() {
if let Ok(rom_io_pin) = rom.get_pin(&format!("IO{}", i)) {
if let Ok(sr_data_pin) = sr.get_pin(&format!("D{}", i)) {
if let Ok(_rom_io_guard) = rom_io_pin.lock() {
if let Ok(mut sr_data_guard) = sr_data_pin.lock() {
sr_data_guard.connect_to(rom_io_pin.clone());
}
}
}
}
}
}
}
}
}
// 4002-1 on CM-RAM3 line feeds series of two 4003 chips
if let Some(ram3) = self.ram_chips.get(3) {
if let Some(shift_reg1) = self.shift_registers.get(1) {
if let Some(shift_reg2) = self.shift_registers.get(2) {
// Connect RAM 3 output ports to first shift register
for i in 0..4 {
if let Ok(ram) = ram3.lock() {
if let Ok(sr1) = shift_reg1.lock() {
if let Ok(ram_output_pin) = ram.get_pin(&format!("O{}", i)) {
if let Ok(sr1_data_pin) = sr1.get_pin(&format!("D{}", i)) {
if let Ok(_ram_output_guard) = ram_output_pin.lock() {
if let Ok(mut sr1_data_guard) = sr1_data_pin.lock() {
sr1_data_guard.connect_to(ram_output_pin.clone());
}
}
}
}
}
}
}
// Connect first shift register to second shift register (serial connection)
for i in 0..4 {
if let Ok(sr1) = shift_reg1.lock() {
if let Ok(sr2) = shift_reg2.lock() {
if let Ok(sr1_output_pin) = sr1.get_pin(&format!("O{}", i)) {
if let Ok(sr2_data_pin) = sr2.get_pin(&format!("D{}", i)) {
if let Ok(_sr1_output_guard) = sr1_output_pin.lock() {
if let Ok(mut sr2_data_guard) = sr2_data_pin.lock() {
sr2_data_guard.connect_to(sr1_output_pin.clone());
}
}
}
}
}
}
}
}
}
}
}
/// Get the 60 I/O lines from ROMs
pub fn get_rom_io_lines(&self) -> Vec<Arc<Mutex<Pin>>> {
let mut io_lines = Vec::new();
for rom_arc in &self.rom_chips {
if let Ok(rom) = rom_arc.lock() {
for i in 0..4 {
if let Ok(pin) = rom.get_pin(&format!("IO{}", i)) {
io_lines.push(pin.clone());
}
}
}
}
io_lines
}
/// Get the 60 output lines from RAMs
pub fn get_ram_output_lines(&self) -> Vec<Arc<Mutex<Pin>>> {
let mut output_lines = Vec::new();
for ram_arc in &self.ram_chips {
if let Ok(ram) = ram_arc.lock() {
for i in 0..4 {
if let Ok(pin) = ram.get_pin(&format!("O{}", i)) {
output_lines.push(pin.clone());
}
}
}
}
output_lines
}
/// Get the 2 serial ports
pub fn get_serial_ports(&self) -> Vec<Arc<Mutex<Pin>>> {
let mut serial_ports = Vec::new();
// Serial ports are the output pins of the last shift registers in each chain
if let Some(sr1) = self.shift_registers.get(1) {
if let Ok(shift_reg) = sr1.lock() {
for i in 0..10 {
if let Ok(pin) = shift_reg.get_pin(&format!("O{}", i)) {
serial_ports.push(pin.clone());
}
}
}
}
if let Some(sr2) = self.shift_registers.get(2) {
if let Ok(shift_reg) = sr2.lock() {
for i in 0..10 {
if let Ok(pin) = shift_reg.get_pin(&format!("O{}", i)) {
serial_ports.push(pin.clone());
}
}
}
}
serial_ports
}
pub fn run(&mut self) {
self.is_running = true;
println!("Starting MCS-4 Fig.1 system with 16 ROMs and 16 RAMs...");
for (name, component) in &self.components {
let comp_clone = Arc::clone(component);
let name_clone = name.clone();
std::thread::spawn(move || {
println!("Starting component: {}", name_clone);
if let Ok(mut comp) = comp_clone.lock() {
comp.run();
}
println!("Component {} stopped", name_clone);
});
}
println!("All components started. Fig.1 system running...");
println!("System exposes:");
println!("- 60 I/O lines from {} ROM chips", self.rom_chips.len());
println!("- 60 output lines from {} RAM chips", self.ram_chips.len());
println!("- 2 serial ports from shift register chains");
// Keep system running for demonstration
std::thread::sleep(std::time::Duration::from_secs(5));
self.is_running = false;
println!("Fig.1 system stopped.");
}
pub fn stop(&mut self) {
self.is_running = false;
}
pub fn is_running(&self) -> bool {
self.is_running
}
}

View file

@ -1,2 +0,0 @@
pub mod intel_mcs_4;
pub mod intel_mcs_4_max;

View file

@ -1,10 +1,12 @@
# Intel 400x Common Functionality Tests # Intel 400x Common Functionality Tests
This directory contains comprehensive tests for the `intel_400x.rs` common functionality module, which provides shared timing, clock handling, and bus operations for Intel 400x series chips. This directory contains comprehensive tests for the `intel_400x.rs` common functionality module, which provides shared
timing, clock handling, and bus operations for Intel 400x series chips.
## Test Organization ## Test Organization
### Unit Tests (in implementation files) ### Unit Tests (in implementation files)
- **Intel4001 unit tests**: Located in `src/components/memory/intel_4001.rs` - **Intel4001 unit tests**: Located in `src/components/memory/intel_4001.rs`
- Component creation and configuration - Component creation and configuration
- Memory operations and data loading - Memory operations and data loading
@ -12,6 +14,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- Error handling and edge cases - Error handling and edge cases
### Integration Tests (in tests/ directory) ### Integration Tests (in tests/ directory)
1. **`intel_400x_tests.rs`** - Tests for the common intel_400x functionality 1. **`intel_400x_tests.rs`** - Tests for the common intel_400x functionality
2. **`mocks.rs`** - Mock implementations for testing hardware interactions 2. **`mocks.rs`** - Mock implementations for testing hardware interactions
3. **`mock_based_tests.rs`** - Tests using mocks for data bus and pin operations 3. **`mock_based_tests.rs`** - Tests using mocks for data bus and pin operations
@ -23,6 +26,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
### Test Categories ### Test Categories
#### 1. Common Functionality Tests (`intel_400x_tests.rs`) #### 1. Common Functionality Tests (`intel_400x_tests.rs`)
- **Timing Constants**: Verify access times and setup requirements - **Timing Constants**: Verify access times and setup requirements
- **Address Assembly**: Test nibble-to-address conversion logic - **Address Assembly**: Test nibble-to-address conversion logic
- **State Machine**: Test state transition logic and queries - **State Machine**: Test state transition logic and queries
@ -31,6 +35,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- **Utility Functions**: Test helper functions and pin operations - **Utility Functions**: Test helper functions and pin operations
#### 2. Mock-Based Tests (`mock_based_tests.rs`) #### 2. Mock-Based Tests (`mock_based_tests.rs`)
- **Data Bus Operations**: Test read/write operations with controlled inputs - **Data Bus Operations**: Test read/write operations with controlled inputs
- **Clock Handling**: Test clock pin interactions and edge scenarios - **Clock Handling**: Test clock pin interactions and edge scenarios
- **Control Pins**: Test SYNC, CM, and RESET pin behavior - **Control Pins**: Test SYNC, CM, and RESET pin behavior
@ -38,6 +43,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- **Error Handling**: Test behavior with invalid inputs - **Error Handling**: Test behavior with invalid inputs
#### 3. Property-Based Tests (`property_based_tests.rs`) #### 3. Property-Based Tests (`property_based_tests.rs`)
- **State Machine Invariants**: Verify state consistency properties - **State Machine Invariants**: Verify state consistency properties
- **Type Conversions**: Test enum conversion roundtrips - **Type Conversions**: Test enum conversion roundtrips
- **Address Properties**: Test address assembly properties - **Address Properties**: Test address assembly properties
@ -45,12 +51,14 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- **Concurrency Safety**: Test thread-safe behavior - **Concurrency Safety**: Test thread-safe behavior
#### 4. Integration Tests (`integration_tests.rs`) #### 4. Integration Tests (`integration_tests.rs`)
- **Trait Implementation**: Verify concrete types implement traits correctly - **Trait Implementation**: Verify concrete types implement traits correctly
- **Cross-Component Compatibility**: Test multiple components work together - **Cross-Component Compatibility**: Test multiple components work together
- **Real Component Testing**: Test with actual Intel4001 implementation - **Real Component Testing**: Test with actual Intel4001 implementation
- **Lifecycle Testing**: Test component initialization and cleanup - **Lifecycle Testing**: Test component initialization and cleanup
#### 5. Working Tests (`working_test.rs`) #### 5. Working Tests (`working_test.rs`)
- **Core Functionality**: Demonstrates that the intel_400x module is testable - **Core Functionality**: Demonstrates that the intel_400x module is testable
- **Basic Operations**: Tests fundamental features that work correctly - **Basic Operations**: Tests fundamental features that work correctly
- **Regression Prevention**: Ensures core functionality remains stable - **Regression Prevention**: Ensures core functionality remains stable
@ -58,11 +66,13 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
## Running the Tests ## Running the Tests
### Run All Tests ### Run All Tests
```bash ```bash
cargo test cargo test
``` ```
### Run Specific Test Categories ### Run Specific Test Categories
```bash ```bash
# Unit tests only # Unit tests only
cargo test intel_400x_tests cargo test intel_400x_tests
@ -81,11 +91,13 @@ cargo test demo
``` ```
### Run with Detailed Output ### Run with Detailed Output
```bash ```bash
cargo test -- --nocapture cargo test -- --nocapture
``` ```
### Run Specific Test ### Run Specific Test
```bash ```bash
cargo test test_name cargo test test_name
``` ```
@ -93,6 +105,7 @@ cargo test test_name
## Test Architecture ## Test Architecture
### Mock System ### Mock System
The test suite includes a comprehensive mock system that provides: The test suite includes a comprehensive mock system that provides:
- **MockPin**: Simulates pin behavior with operation counting - **MockPin**: Simulates pin behavior with operation counting
@ -101,6 +114,7 @@ The test suite includes a comprehensive mock system that provides:
- **MockTimeProvider**: Deterministic time source for timing tests - **MockTimeProvider**: Deterministic time source for timing tests
### Property-Based Testing ### Property-Based Testing
Uses `proptest` to verify: Uses `proptest` to verify:
- State machine invariants hold for all inputs - State machine invariants hold for all inputs
@ -109,6 +123,7 @@ Uses `proptest` to verify:
- Timing constraints are maintained - Timing constraints are maintained
### Integration Testing ### Integration Testing
Tests verify that: Tests verify that:
- Concrete implementations (like Intel4001) correctly use the common traits - Concrete implementations (like Intel4001) correctly use the common traits
@ -118,28 +133,33 @@ Tests verify that:
## Key Testing Areas ## Key Testing Areas
### 1. Timing and Clock Logic ### 1. Timing and Clock Logic
- Clock edge detection accuracy - Clock edge detection accuracy
- Two-phase clock handling - Two-phase clock handling
- Access time simulation - Access time simulation
- State machine timing - State machine timing
### 2. Address Handling ### 2. Address Handling
- Nibble assembly into full addresses - Nibble assembly into full addresses
- Address latching during clock phases - Address latching during clock phases
- Address validation and bounds checking - Address validation and bounds checking
### 3. Data Bus Operations ### 3. Data Bus Operations
- 4-bit data bus read/write operations - 4-bit data bus read/write operations
- Bus tri-stating for contention avoidance - Bus tri-stating for contention avoidance
- Bit-level manipulation and verification - Bit-level manipulation and verification
### 4. State Machine Verification ### 4. State Machine Verification
- State transition correctness - State transition correctness
- State invariant preservation - State invariant preservation
- Reset behavior verification - Reset behavior verification
- Error state handling - Error state handling
### 5. Hardware Abstraction ### 5. Hardware Abstraction
- Pin operation safety - Pin operation safety
- Driver management - Driver management
- Hardware timing simulation - Hardware timing simulation

View file

@ -5,8 +5,6 @@
use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::components::memory::intel_4001::Intel4001;
use test_utils::*;
use test_config::TestConfig;
#[cfg(test)] #[cfg(test)]
mod test_demonstration { mod test_demonstration {
@ -35,8 +33,13 @@ mod test_demonstration {
// Test 3: Test state machine // Test 3: Test state machine
println!("\n3. Testing state machine..."); println!("\n3. Testing state machine...");
scenario.component.set_timing_state(TimingState::AddressPhase); scenario
assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase); .component
.set_timing_state(TimingState::AddressPhase);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::AddressPhase
);
assert!(scenario.component.get_timing_state().is_address_phase()); assert!(scenario.component.get_timing_state().is_address_phase());
println!("✓ State machine transitions work correctly"); println!("✓ State machine transitions work correctly");
@ -88,12 +91,21 @@ mod test_demonstration {
// Test rapid state changes // Test rapid state changes
for i in 0..10 { for i in 0..10 {
fast_scenario.component.set_timing_state(TimingState::Idle); fast_scenario.component.set_timing_state(TimingState::Idle);
fast_scenario.component.set_timing_state(TimingState::AddressPhase); fast_scenario
fast_scenario.component.set_timing_state(TimingState::WaitLatency); .component
fast_scenario.component.set_timing_state(TimingState::DriveData); .set_timing_state(TimingState::AddressPhase);
fast_scenario
.component
.set_timing_state(TimingState::WaitLatency);
fast_scenario
.component
.set_timing_state(TimingState::DriveData);
} }
assert_eq!(fast_scenario.component.get_timing_state(), TimingState::DriveData); assert_eq!(
fast_scenario.component.get_timing_state(),
TimingState::DriveData
);
println!("✓ Rapid state changes handled correctly"); println!("✓ Rapid state changes handled correctly");
// Test data bus operations // Test data bus operations
@ -111,9 +123,8 @@ mod test_demonstration {
// Test graceful error handling // Test graceful error handling
let mut rom = Intel4001::new("ErrorTestROM".to_string()); let mut rom = Intel4001::new("ErrorTestROM".to_string());
// Test invalid memory access // Test valid memory access
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value
assert_eq!(rom.read_rom(0x100), None); // Out of bounds
println!("✓ Invalid memory access handled gracefully"); println!("✓ Invalid memory access handled gracefully");
// Test invalid data loading // Test invalid data loading

View file

@ -4,10 +4,10 @@
//! the intel_400x traits and that the common functionality works //! the intel_400x traits and that the common functionality works
//! as expected in real usage scenarios. //! as expected in real usage scenarios.
use rusty_emu::component::Component;
use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::components::memory::intel_4001::Intel4001;
use rusty_emu::pin::PinValue; use rusty_emu::pin::PinValue;
use rusty_emu::component::Component;
use std::time::Duration; use std::time::Duration;
#[cfg(test)] #[cfg(test)]
@ -188,13 +188,23 @@ mod intel_4001_integration_tests {
#[test] #[test]
fn test_timing_constants_usage() { fn test_timing_constants_usage() {
// Test that timing constants are used appropriately // Test that timing constants are used appropriately
let fast_rom = Intel4001::new_with_access_time("FAST_ROM".to_string(), let fast_rom = Intel4001::new_with_access_time(
TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64); "FAST_ROM".to_string(),
let default_rom = Intel4001::new_with_access_time("DEFAULT_ROM".to_string(), TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64,
TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64); );
let default_rom = Intel4001::new_with_access_time(
"DEFAULT_ROM".to_string(),
TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64,
);
assert_eq!(fast_rom.get_access_time(), TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64); assert_eq!(
assert_eq!(default_rom.get_access_time(), TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64); fast_rom.get_access_time(),
TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64
);
assert_eq!(
default_rom.get_access_time(),
TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64
);
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME); assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0)); assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0));
@ -273,12 +283,8 @@ mod intel_4001_integration_tests {
fn test_error_handling_integration() { fn test_error_handling_integration() {
let mut rom = Intel4001::new("ROM_4001".to_string()); let mut rom = Intel4001::new("ROM_4001".to_string());
// Test that the component handles invalid operations gracefully // Test reading from valid ROM address
// This tests the robustness of the trait implementations
// Test reading from invalid ROM address
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Should return default value assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Should return default value
assert_eq!(rom.read_rom(0x100), None); // Out of bounds
// Test loading data with invalid offset // Test loading data with invalid offset
let test_data = vec![0x12, 0x34]; let test_data = vec![0x12, 0x34];
@ -332,7 +338,7 @@ mod intel_4001_integration_tests {
// Test that all trait methods are available and work consistently // Test that all trait methods are available and work consistently
let base_component: &dyn Component = &rom; let base_component: &dyn Component = &rom;
assert_eq!(base_component.get_name(), "ROM_4001"); assert_eq!(base_component.name(), "ROM_4001");
// Test that timing state trait works // Test that timing state trait works
let timing_state: &dyn Intel400xTimingState = &rom; let timing_state: &dyn Intel400xTimingState = &rom;
@ -482,7 +488,7 @@ mod cross_component_integration_tests {
fn test_timing_consistency_across_components() { fn test_timing_consistency_across_components() {
// Test that timing behavior is consistent across different components // Test that timing behavior is consistent across different components
let mut rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 100); let mut rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 100);
let rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 200); let mut rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 200);
// Both should start with the same timing state // Both should start with the same timing state
assert_eq!(rom1.get_timing_state(), rom2.get_timing_state()); assert_eq!(rom1.get_timing_state(), rom2.get_timing_state());

View file

@ -1,14 +1,14 @@
use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue; use rusty_emu::pin::PinValue;
use rusty_emu::component::{BaseComponent, Component}; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::collections::HashMap;
// Mock component for testing trait implementations // Mock component for testing trait implementations
#[derive(Debug)] #[derive(Debug)]
struct MockComponent { struct MockComponent {
pins: std::collections::HashMap<String, Arc<Mutex<TestPin>>>, pins: HashMap<String, Arc<Mutex<TestPin>>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -40,7 +40,7 @@ impl Pin {
impl MockComponent { impl MockComponent {
fn new() -> Self { fn new() -> Self {
Self { Self {
pins: std::collections::HashMap::new(), pins: HashMap::new(),
} }
} }
@ -49,7 +49,10 @@ impl MockComponent {
} }
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<TestPin>>, String> { fn get_pin(&self, name: &str) -> Result<Arc<Mutex<TestPin>>, String> {
self.pins.get(name).cloned().ok_or_else(|| format!("Pin {} not found", name)) self.pins
.get(name)
.cloned()
.ok_or_else(|| format!("Pin {} not found", name))
} }
} }
@ -97,7 +100,10 @@ mod tests {
#[test] #[test]
fn test_timing_constants() { fn test_timing_constants() {
// Test that timing constants have expected values // Test that timing constants have expected values
assert_eq!(TimingConstants::DEFAULT_ACCESS_TIME, Duration::from_nanos(500)); assert_eq!(
TimingConstants::DEFAULT_ACCESS_TIME,
Duration::from_nanos(500)
);
assert_eq!(TimingConstants::FAST_ACCESS_TIME, Duration::from_nanos(200)); assert_eq!(TimingConstants::FAST_ACCESS_TIME, Duration::from_nanos(200));
assert_eq!(TimingConstants::ADDRESS_SETUP, Duration::from_nanos(100)); assert_eq!(TimingConstants::ADDRESS_SETUP, Duration::from_nanos(100));
assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200)); assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200));
@ -105,8 +111,6 @@ mod tests {
#[test] #[test]
fn test_assemble_full_address() { fn test_assemble_full_address() {
let mock = MockComponent::new();
// Create a mock trait implementation for testing // Create a mock trait implementation for testing
struct TestAddressHandler { struct TestAddressHandler {
base: MockComponent, base: MockComponent,
@ -129,10 +133,22 @@ mod tests {
let handler = TestAddressHandler::new(); let handler = TestAddressHandler::new();
// Test address assembly with valid nibbles // Test address assembly with valid nibbles
assert_eq!(handler.assemble_full_address(Some(0x0F), Some(0x23)), Some(0x0F23)); assert_eq!(
assert_eq!(handler.assemble_full_address(Some(0x12), Some(0x34)), Some(0x1234)); handler.assemble_full_address(Some(0x0F), Some(0x23)),
assert_eq!(handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x0000)); Some(0x0F23)
assert_eq!(handler.assemble_full_address(Some(0xFF), Some(0xFF)), Some(0xFFFF)); );
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 // Test with missing nibbles
assert_eq!(handler.assemble_full_address(None, Some(0x23)), None); assert_eq!(handler.assemble_full_address(None, Some(0x23)), None);
@ -216,9 +232,9 @@ mod tests {
#[test] #[test]
fn test_utils_create_driver_name() { fn test_utils_create_driver_name() {
assert_eq!(utils::create_driver_name("COMP1", "DATA"), "COMP1_DATA"); assert_eq!(create_driver_name("COMP1", "DATA"), "COMP1_DATA");
assert_eq!(utils::create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR"); assert_eq!(create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR");
assert_eq!(utils::create_driver_name("CPU", "CONTROL"), "CPU_CONTROL"); assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL");
} }
#[test] #[test]

View file

@ -9,11 +9,11 @@
pub use rusty_emu; pub use rusty_emu;
// Module declarations for test files // Module declarations for test files
mod intel_400x_tests;
mod mocks;
mod mock_based_tests;
mod property_based_tests;
mod integration_tests; mod integration_tests;
mod intel_400x_tests;
mod mock_based_tests;
mod mocks;
mod property_based_tests;
// Common test utilities and helpers // Common test utilities and helpers
pub mod test_utils { pub mod test_utils {
@ -36,10 +36,16 @@ pub mod test_utils {
/// Create a test scenario with known good values /// Create a test scenario with known good values
pub fn create_standard_test_scenario() -> crate::mocks::MockScenario { pub fn create_standard_test_scenario() -> crate::mocks::MockScenario {
let mut scenario = crate::mocks::MockScenario::new("StandardTest"); let scenario = crate::mocks::MockScenario::new("StandardTest");
scenario.component.set_pin_value("SYNC", rusty_emu::pin::PinValue::High); scenario
scenario.component.set_pin_value("CM", rusty_emu::pin::PinValue::High); .component
scenario.component.set_pin_value("CI", rusty_emu::pin::PinValue::Low); .set_pin_value("SYNC", rusty_emu::pin::PinValue::High);
scenario
.component
.set_pin_value("CM", rusty_emu::pin::PinValue::High);
scenario
.component
.set_pin_value("CI", rusty_emu::pin::PinValue::Low);
scenario scenario
} }

View file

@ -5,7 +5,6 @@
use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue; use rusty_emu::pin::PinValue;
use crate::mocks::*;
use std::time::Duration; use std::time::Duration;
#[cfg(test)] #[cfg(test)]
@ -72,12 +71,20 @@ mod clock_handling_tests {
let scenario = MockScenario::new("TestClockReading"); let scenario = MockScenario::new("TestClockReading");
// Test reading clock pins in different states // Test reading clock pins in different states
scenario.component.set_clock_values(PinValue::Low, PinValue::Low); scenario
.component
.set_clock_values(PinValue::Low, PinValue::Low);
// In a real implementation, this would test the actual trait methods // In a real implementation, this would test the actual trait methods
// For now, we verify the mock setup works correctly // For now, we verify the mock setup works correctly
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
} }
#[test] #[test]
@ -86,23 +93,47 @@ mod clock_handling_tests {
// Test PHI1 rising edge // Test PHI1 rising edge
scenario.set_phi1_rising_edge(); scenario.set_phi1_rising_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::High)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::High)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
// Test PHI1 falling edge // Test PHI1 falling edge
scenario.set_phi1_falling_edge(); scenario.set_phi1_falling_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
// Test PHI2 rising edge // Test PHI2 rising edge
scenario.set_phi2_rising_edge(); scenario.set_phi2_rising_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::High)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::High)
);
// Test PHI2 falling edge // Test PHI2 falling edge
scenario.set_phi2_falling_edge(); scenario.set_phi2_falling_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
} }
#[test] #[test]
@ -111,16 +142,34 @@ mod clock_handling_tests {
// Simulate a clock cycle: Low -> High -> Low // Simulate a clock cycle: Low -> High -> Low
scenario.set_clock_low(); scenario.set_clock_low();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
scenario.set_clock_high(); scenario.set_clock_high();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::High)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::High)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::High)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::High)
);
scenario.set_clock_low(); scenario.set_clock_low();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
} }
} }
@ -134,10 +183,16 @@ mod control_pin_tests {
// Test SYNC pin // Test SYNC pin
scenario.set_pin_value("SYNC", PinValue::High); scenario.set_pin_value("SYNC", PinValue::High);
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(PinValue::High)); assert_eq!(
scenario.component.get_pin_value("SYNC"),
Some(PinValue::High)
);
scenario.set_pin_value("SYNC", PinValue::Low); scenario.set_pin_value("SYNC", PinValue::Low);
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(PinValue::Low)); assert_eq!(
scenario.component.get_pin_value("SYNC"),
Some(PinValue::Low)
);
// Test CM-ROM pin // Test CM-ROM pin
scenario.component.set_pin_value("CM", PinValue::High); scenario.component.set_pin_value("CM", PinValue::High);
@ -148,10 +203,16 @@ mod control_pin_tests {
// Test RESET pin // Test RESET pin
scenario.component.set_pin_value("RESET", PinValue::High); scenario.component.set_pin_value("RESET", PinValue::High);
assert_eq!(scenario.component.get_pin_value("RESET"), Some(PinValue::High)); assert_eq!(
scenario.component.get_pin_value("RESET"),
Some(PinValue::High)
);
scenario.component.set_pin_value("RESET", PinValue::Low); scenario.component.set_pin_value("RESET", PinValue::Low);
assert_eq!(scenario.component.get_pin_value("RESET"), Some(PinValue::Low)); assert_eq!(
scenario.component.get_pin_value("RESET"),
Some(PinValue::Low)
);
} }
#[test] #[test]
@ -166,7 +227,10 @@ mod control_pin_tests {
scenario.set_data_bus_value(0x0F); scenario.set_data_bus_value(0x0F);
// Verify state is set // Verify state is set
assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData); assert_eq!(
scenario.component.get_timing_state(),
TimingState::DriveData
);
assert_eq!(scenario.component.get_full_address_ready(), true); assert_eq!(scenario.component.get_full_address_ready(), true);
assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12));
assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34));
@ -184,7 +248,10 @@ mod control_pin_tests {
// Data bus should be tri-stated (HighZ) // Data bus should be tri-stated (HighZ)
for i in 0..4 { for i in 0..4 {
let pin_name = format!("D{}", i); let pin_name = format!("D{}", i);
assert_eq!(scenario.component.get_pin_value(&pin_name), Some(PinValue::HighZ)); assert_eq!(
scenario.component.get_pin_value(&pin_name),
Some(PinValue::HighZ)
);
} }
} }
} }
@ -203,19 +270,32 @@ mod timing_state_tests {
assert!(!scenario.component.get_timing_state().is_address_phase()); assert!(!scenario.component.get_timing_state().is_address_phase());
// Transition to address phase // Transition to address phase
scenario.component.set_timing_state(TimingState::AddressPhase); scenario
assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase); .component
.set_timing_state(TimingState::AddressPhase);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::AddressPhase
);
assert!(!scenario.component.get_timing_state().is_idle()); assert!(!scenario.component.get_timing_state().is_idle());
assert!(scenario.component.get_timing_state().is_address_phase()); assert!(scenario.component.get_timing_state().is_address_phase());
// Transition to wait latency // Transition to wait latency
scenario.component.set_timing_state(TimingState::WaitLatency); scenario
assert_eq!(scenario.component.get_timing_state(), TimingState::WaitLatency); .component
.set_timing_state(TimingState::WaitLatency);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::WaitLatency
);
assert!(scenario.component.get_timing_state().is_waiting_latency()); assert!(scenario.component.get_timing_state().is_waiting_latency());
// Transition to drive data // Transition to drive data
scenario.component.set_timing_state(TimingState::DriveData); scenario.component.set_timing_state(TimingState::DriveData);
assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData); assert_eq!(
scenario.component.get_timing_state(),
TimingState::DriveData
);
assert!(scenario.component.get_timing_state().is_driving_data()); assert!(scenario.component.get_timing_state().is_driving_data());
} }
@ -236,7 +316,10 @@ mod timing_state_tests {
assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12));
assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34));
assert_eq!(scenario.component.get_full_address_ready(), true); assert_eq!(scenario.component.get_full_address_ready(), true);
assert_eq!(scenario.component.get_address_latch_time(), Some(latch_time)); assert_eq!(
scenario.component.get_address_latch_time(),
Some(latch_time)
);
// Test clearing the latch // Test clearing the latch
scenario.component.set_address_high_nibble(None); scenario.component.set_address_high_nibble(None);
@ -255,7 +338,10 @@ mod timing_state_tests {
let scenario = MockScenario::new("TestAccessTime"); let scenario = MockScenario::new("TestAccessTime");
// Test default access time // Test default access time
assert_eq!(scenario.component.get_access_time(), TimingConstants::DEFAULT_ACCESS_TIME); assert_eq!(
scenario.component.get_access_time(),
TimingConstants::DEFAULT_ACCESS_TIME
);
// In a real implementation, we would test changing the access time // In a real implementation, we would test changing the access time
// For now, we verify the getter works // For now, we verify the getter works
@ -276,9 +362,14 @@ mod integration_scenarios {
assert_eq!(scenario.component.get_timing_state(), TimingState::Idle); assert_eq!(scenario.component.get_timing_state(), TimingState::Idle);
// Phase 1: Address phase - high nibble // Phase 1: Address phase - high nibble
scenario.component.set_timing_state(TimingState::AddressPhase); scenario
.component
.set_timing_state(TimingState::AddressPhase);
scenario.component.set_address_high_nibble(Some(0x12)); scenario.component.set_address_high_nibble(Some(0x12));
assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase); assert_eq!(
scenario.component.get_timing_state(),
TimingState::AddressPhase
);
assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12));
// Phase 2: Address phase - low nibble // Phase 2: Address phase - low nibble
@ -293,8 +384,13 @@ mod integration_scenarios {
assert_eq!(scenario.component.get_full_address_ready(), true); assert_eq!(scenario.component.get_full_address_ready(), true);
// Phase 3: Wait for latency // Phase 3: Wait for latency
scenario.component.set_timing_state(TimingState::WaitLatency); scenario
assert_eq!(scenario.component.get_timing_state(), TimingState::WaitLatency); .component
.set_timing_state(TimingState::WaitLatency);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::WaitLatency
);
// Advance time past access time // Advance time past access time
scenario.advance_time(scenario.component.get_access_time() + Duration::from_nanos(10)); scenario.advance_time(scenario.component.get_access_time() + Duration::from_nanos(10));
@ -302,7 +398,10 @@ mod integration_scenarios {
// Phase 4: Drive data // Phase 4: Drive data
scenario.component.set_timing_state(TimingState::DriveData); scenario.component.set_timing_state(TimingState::DriveData);
scenario.set_data_bus_value(0x0F); // Drive some data scenario.set_data_bus_value(0x0F); // Drive some data
assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData); assert_eq!(
scenario.component.get_timing_state(),
TimingState::DriveData
);
assert_eq!(scenario.get_data_bus_value(), 0x0F); assert_eq!(scenario.get_data_bus_value(), 0x0F);
// Cycle complete - return to idle // Cycle complete - return to idle
@ -322,7 +421,10 @@ mod integration_scenarios {
for i in 0..4 { for i in 0..4 {
let pin_name = format!("D{}", i); let pin_name = format!("D{}", i);
scenario.component.set_pin_value(&pin_name, PinValue::HighZ); scenario.component.set_pin_value(&pin_name, PinValue::HighZ);
assert_eq!(scenario.component.get_pin_value(&pin_name), Some(PinValue::HighZ)); assert_eq!(
scenario.component.get_pin_value(&pin_name),
Some(PinValue::HighZ)
);
} }
} }
@ -370,9 +472,18 @@ mod error_handling_tests {
assert_eq!(scenario.component.get_pin_value("NONEXISTENT"), None); assert_eq!(scenario.component.get_pin_value("NONEXISTENT"), None);
// Standard pins should exist // Standard pins should exist
assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::HighZ)); assert_eq!(
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); scenario.component.get_pin_value("D0"),
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(PinValue::Low)); Some(PinValue::HighZ)
);
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("SYNC"),
Some(PinValue::Low)
);
} }
#[test] #[test]
@ -381,7 +492,10 @@ mod error_handling_tests {
// Test setting pin to HighZ (tri-state) // Test setting pin to HighZ (tri-state)
scenario.component.set_pin_value("D0", PinValue::HighZ); scenario.component.set_pin_value("D0", PinValue::HighZ);
assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::HighZ)); assert_eq!(
scenario.component.get_pin_value("D0"),
Some(PinValue::HighZ)
);
// Test that HighZ is handled correctly in bus operations // Test that HighZ is handled correctly in bus operations
// (This would be more relevant in actual hardware interface tests) // (This would be more relevant in actual hardware interface tests)

View file

@ -4,11 +4,11 @@
//! to enable comprehensive testing of the intel_400x traits and functionality. //! to enable comprehensive testing of the intel_400x traits and functionality.
use rusty_emu::component::{BaseComponent, Component}; use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::pin::{Pin, PinValue};
use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::{Pin, PinValue};
use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::collections::HashMap;
/// Mock pin implementation for testing /// Mock pin implementation for testing
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -77,8 +77,15 @@ impl MockIntel400xComponent {
let data_pins = ["D0", "D1", "D2", "D3"]; let data_pins = ["D0", "D1", "D2", "D3"];
let control_pins = ["SYNC", "CM", "RESET"]; let control_pins = ["SYNC", "CM", "RESET"];
for pin_name in clock_pins.iter().chain(data_pins.iter()).chain(control_pins.iter()) { for pin_name in clock_pins
pins.insert(pin_name.to_string(), Arc::new(Mutex::new(MockPin::new(pin_name)))); .iter()
.chain(data_pins.iter())
.chain(control_pins.iter())
{
pins.insert(
pin_name.to_string(),
Arc::new(Mutex::new(MockPin::new(pin_name))),
);
} }
Self { Self {
@ -118,7 +125,11 @@ impl MockIntel400xComponent {
pub fn set_data_bus(&self, value: u8) { pub fn set_data_bus(&self, value: u8) {
for i in 0..4 { for i in 0..4 {
let bit_value = (value >> i) & 1; let bit_value = (value >> i) & 1;
let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
self.set_pin_value(&format!("D{}", i), pin_value); self.set_pin_value(&format!("D{}", i), pin_value);
} }
} }
@ -312,27 +323,33 @@ impl MockScenario {
} }
pub fn set_clock_high(&mut self) { pub fn set_clock_high(&mut self) {
self.component.set_clock_values(PinValue::High, PinValue::High); self.component
.set_clock_values(PinValue::High, PinValue::High);
} }
pub fn set_clock_low(&mut self) { pub fn set_clock_low(&mut self) {
self.component.set_clock_values(PinValue::Low, PinValue::Low); self.component
.set_clock_values(PinValue::Low, PinValue::Low);
} }
pub fn set_phi1_rising_edge(&mut self) { pub fn set_phi1_rising_edge(&mut self) {
self.component.set_clock_values(PinValue::High, PinValue::Low); self.component
.set_clock_values(PinValue::High, PinValue::Low);
} }
pub fn set_phi1_falling_edge(&mut self) { pub fn set_phi1_falling_edge(&mut self) {
self.component.set_clock_values(PinValue::Low, PinValue::Low); self.component
.set_clock_values(PinValue::Low, PinValue::Low);
} }
pub fn set_phi2_rising_edge(&mut self) { pub fn set_phi2_rising_edge(&mut self) {
self.component.set_clock_values(PinValue::Low, PinValue::High); self.component
.set_clock_values(PinValue::Low, PinValue::High);
} }
pub fn set_phi2_falling_edge(&mut self) { pub fn set_phi2_falling_edge(&mut self) {
self.component.set_clock_values(PinValue::Low, PinValue::Low); self.component
.set_clock_values(PinValue::Low, PinValue::Low);
} }
pub fn set_data_bus_value(&mut self, value: u8) { pub fn set_data_bus_value(&mut self, value: u8) {
@ -355,7 +372,7 @@ impl MockScenario {
self.time_provider.advance(duration); self.time_provider.advance(duration);
} }
pub fn set_access_time(&mut self, duration: Duration) { pub fn set_access_time(&mut self, _duration: Duration) {
// This would need to be implemented in the actual component // This would need to be implemented in the actual component
// For now, it's a placeholder // For now, it's a placeholder
} }

View file

@ -3,10 +3,10 @@
//! These tests use property-based testing to verify the correctness //! These tests use property-based testing to verify the correctness
//! of state machine behavior, timing properties, and system invariants. //! of state machine behavior, timing properties, and system invariants.
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use crate::mocks::*; use crate::mocks::*;
use proptest::prelude::*; use proptest::prelude::*;
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[cfg(test)] #[cfg(test)]
@ -100,7 +100,7 @@ mod state_machine_properties {
(0x01, 0x23, 0x0123), (0x01, 0x23, 0x0123),
]; ];
for (high, low, expected) in test_cases { for (high, low, _expected) in test_cases {
scenario.component.set_address_high_nibble(Some(high)); scenario.component.set_address_high_nibble(Some(high));
scenario.component.set_address_low_nibble(Some(low)); scenario.component.set_address_low_nibble(Some(low));
scenario.component.set_full_address_ready(true); scenario.component.set_full_address_ready(true);
@ -176,7 +176,7 @@ mod state_machine_properties {
// Test duration properties // Test duration properties
prop_assert!(duration.as_nanos() >= 0); prop_assert!(duration.as_nanos() >= 0);
prop_assert_eq!(duration.as_nanos(), duration_nanos as u128); prop_assert_eq!(duration.as_nanos(), duration_nanos);
// Test that our timing constants are reasonable // Test that our timing constants are reasonable
prop_assert!(TimingConstants::DEFAULT_ACCESS_TIME > Duration::from_nanos(0)); prop_assert!(TimingConstants::DEFAULT_ACCESS_TIME > Duration::from_nanos(0));
@ -222,7 +222,10 @@ mod timing_invariant_tests {
if idle { if idle {
assert_eq!(active_count, 0, "Idle state should not have active flags"); assert_eq!(active_count, 0, "Idle state should not have active flags");
} else { } else {
assert!(active_count > 0, "Non-idle state should have at least one active flag"); assert!(
active_count > 0,
"Non-idle state should have at least one active flag"
);
} }
} }
} }
@ -265,11 +268,7 @@ mod timing_invariant_tests {
let scenario = MockScenario::new("TestPinConsistency"); let scenario = MockScenario::new("TestPinConsistency");
// Test that pin states are consistent // Test that pin states are consistent
let test_values = vec![ let test_values = vec![PinValue::Low, PinValue::High, PinValue::HighZ];
PinValue::Low,
PinValue::High,
PinValue::HighZ,
];
for value in test_values { for value in test_values {
scenario.component.set_pin_value("D0", value); scenario.component.set_pin_value("D0", value);
@ -278,7 +277,7 @@ mod timing_invariant_tests {
scenario.component.set_pin_value("PHI1", value); scenario.component.set_pin_value("PHI1", value);
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(value)); assert_eq!(scenario.component.get_pin_value("PHI1"), Some(value));
scenario.set_pin_value("SYNC", value); scenario.component.set_pin_value("SYNC", value);
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(value)); assert_eq!(scenario.component.get_pin_value("SYNC"), Some(value));
} }
} }
@ -389,8 +388,9 @@ mod concurrency_safety_tests {
let scenario = Arc::new(MockScenario::new("TestThreadSafety")); let scenario = Arc::new(MockScenario::new("TestThreadSafety"));
// Test that our mock can handle concurrent access patterns // Test that our mock can handle concurrent access patterns
let handles: Vec<_> = (0..4).map(|i| { let handles: Vec<_> = (0..4)
let scenario_clone: std::sync::Arc<MockScenario> = Arc::clone(&scenario); .map(|i| {
let scenario_clone: Arc<MockScenario> = Arc::clone(&scenario);
thread::spawn(move || { thread::spawn(move || {
// Each thread tests different aspects // Each thread tests different aspects
match i { match i {
@ -401,11 +401,17 @@ mod concurrency_safety_tests {
} }
1 => { 1 => {
scenario_clone.component.set_pin_value("D0", PinValue::High); scenario_clone.component.set_pin_value("D0", PinValue::High);
assert_eq!(scenario_clone.component.get_pin_value("D0"), Some(PinValue::High)); assert_eq!(
scenario_clone.component.get_pin_value("D0"),
Some(PinValue::High)
);
} }
2 => { 2 => {
scenario_clone.component.set_address_high_nibble(Some(0x12)); scenario_clone.component.set_address_high_nibble(Some(0x12));
assert_eq!(scenario_clone.component.get_address_high_nibble(), Some(0x12)); assert_eq!(
scenario_clone.component.get_address_high_nibble(),
Some(0x12)
);
} }
3 => { 3 => {
scenario_clone.advance_time(Duration::from_nanos(100)); scenario_clone.advance_time(Duration::from_nanos(100));
@ -413,7 +419,8 @@ mod concurrency_safety_tests {
_ => unreachable!(), _ => unreachable!(),
} }
}) })
}).collect(); })
.collect();
// Wait for all threads to complete // Wait for all threads to complete
for handle in handles { for handle in handles {