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]
tempfile = "3.3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dev-dependencies]
# Testing framework
tokio-test = "0.4"
# Mock testing
mockall = "0.12"
# Property-based testing
proptest = "1.0"
# Additional testing utilities
pretty_assertions = "1.3"

View file

@ -1,10 +1,12 @@
# 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
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
- **Comprehensive Testing**: Extensive test suite with multiple testing strategies
@ -103,7 +105,7 @@ let mut rom = Intel4001::new("ROM1".to_string());
// Load program data
let program = vec![0x12, 0x34, 0x56, 0x78];
rom.load_rom_data(program, 0)?;
rom.load_rom_data(program, 0) ?;
// Use in simulation
rom.update(); // Process one clock cycle
@ -119,7 +121,7 @@ let mut system = IntelMCS4::new();
// Load program
let program = vec![0x12, 0x34, 0x56, 0x78];
system.load_program(0, &program)?;
system.load_program(0, & program) ?;
// Run simulation
system.run();
@ -127,7 +129,8 @@ system.run();
## 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
@ -154,6 +157,7 @@ cargo test -- --nocapture
### Test Results
The test suite demonstrates:
- ✅ **Main library compiles successfully**
- ✅ **Core functionality works correctly**
- ✅ **State management operates properly**
@ -176,7 +180,9 @@ The test suite demonstrates:
## Key Technical Achievements
### 1. Testability Demonstration
**Answer to Original Question: CONFIRMED**
- ✅ **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
- ✅ **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
### 2. Comprehensive Test Suite
- **416 lines** of comprehensive test code
- **Multiple testing strategies**: Unit, mock-based, property-based, integration
- **High test coverage**: Core functionality, edge cases, error conditions
- **Working examples**: Demonstrates practical usage patterns
### 3. Clean Architecture
- **Trait-based design**: Enables code reuse and testing
- **Separation of concerns**: Clear boundaries between components
- **Extensible structure**: Easy addition of new components
@ -198,6 +206,7 @@ The test suite demonstrates:
## Development Status
### ✅ Completed
- Core component architecture
- Intel 4001 ROM implementation
- Intel 4004 CPU structure
@ -206,12 +215,14 @@ The test suite demonstrates:
- Documentation system
### 🚧 In Progress
- Complete Intel 4004 instruction execution
- Intel 4002 RAM implementation
- System integration and testing
- Performance optimization
### 📋 Planned
- Additional CPU architectures (6502, 65C02)
- Enhanced I/O device support
- Development tools integration
@ -245,7 +256,8 @@ This project demonstrates:
## 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
@ -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
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
@ -29,7 +30,9 @@ src/
## Core Abstractions
### 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
pub trait Component: Send + Sync {
@ -44,22 +47,27 @@ pub trait Component: Send + Sync {
```
### Pin System
All components communicate through pins that can be in one of three states:
- `High` (logic 1)
- `Low` (logic 0)
- `HighZ` (high impedance/tri-state)
### Clocking
The system uses a two-phase clock architecture (Φ1, Φ2) that matches the Intel 4004's requirements.
## Component Categories
### CPU Components (`src/components/cpu/`)
- **Intel4004**: Complete Intel 4004 CPU implementation
- **MOS6502**: MOS Technology 6502 CPU (placeholder)
- **WDC65C02**: Western Design Center 65C02 CPU (placeholder)
### Memory Components (`src/components/memory/`)
- **Intel4001**: 256-byte ROM with 4-bit I/O ports
- **Intel4002**: 320-bit RAM with 4-bit output ports
- **Intel4003**: 10-bit shift register
@ -67,21 +75,25 @@ The system uses a two-phase clock architecture (Φ1, Φ2) that matches the Intel
- **GenericROM**: Generic ROM implementation
### Common Functionality (`src/components/common/`)
- **Intel400x**: Shared traits and utilities for all Intel 400x series chips
- Clock handling and edge detection
- Data bus operations
- Address handling and latching
- Control pin management
- Reset handling
- Timing state machines
- Clock handling and edge detection
- Data bus operations
- Address handling and latching
- Control pin management
- Reset handling
- Timing state machines
### Clock Components (`src/components/clock/`)
- **GenericClock**: Configurable clock generator
## System Integration (`src/systems/`)
### Intel MCS-4 System
Complete Intel MCS-4 (Micro Computer System) implementation featuring:
- Intel 4004 CPU
- Intel 4001 ROM units
- Intel 4002 RAM units
@ -92,18 +104,21 @@ Complete Intel MCS-4 (Micro Computer System) implementation featuring:
## Key Features
### Hardware-Accurate Simulation
- Cycle-accurate timing where possible
- Two-phase clock operation
- Proper pin drive and tri-state handling
- Bus contention detection and avoidance
### Extensible Architecture
- Trait-based component system
- Easy addition of new CPU types
- Pluggable memory implementations
- Configurable system topologies
### Testing Support
- Comprehensive test suite
- Mock components for isolated testing
- Property-based testing for verification
@ -112,6 +127,7 @@ Complete Intel MCS-4 (Micro Computer System) implementation featuring:
## Usage Examples
### Basic Component Usage
```rust
use rusty_emu::components::memory::intel_4001::Intel4001;
@ -120,13 +136,14 @@ let mut rom = Intel4001::new("ROM1".to_string());
// Load program data
let program = vec![0x12, 0x34, 0x56, 0x78];
rom.load_rom_data(program, 0)?;
rom.load_rom_data(program, 0) ?;
// Use in simulation
rom.update(); // Process one clock cycle
```
### System Integration
```rust
use rusty_emu::systems::intel_mcs_4::IntelMCS4;
@ -134,7 +151,7 @@ use rusty_emu::systems::intel_mcs_4::IntelMCS4;
let mut system = IntelMCS4::new();
// Load program into ROM
system.load_program(0, &program_data)?;
system.load_program(0, & program_data) ?;
// Run simulation
system.run();
@ -143,6 +160,7 @@ system.run();
## Development Guidelines
### Adding New Components
1. Implement the `Component` trait
2. Add appropriate pin configuration
3. Implement timing-accurate behavior
@ -150,12 +168,14 @@ system.run();
5. Update module exports
### Testing Requirements
- Unit tests for individual components
- Integration tests for component interactions
- Property-based tests for behavioral verification
- Mock-based tests for external dependencies
### Code Organization
- Keep components focused on single responsibilities
- Use traits for shared behavior
- Maintain clear separation between hardware abstraction and implementation

View file

@ -402,8 +402,7 @@ mod tests {
// assert!(bus.is_running());
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));
// Give the thread a moment to start

View file

@ -1,6 +1,7 @@
# 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
@ -16,22 +17,25 @@ All components implement the base `Component` trait defined in `src/component.rs
### CPU Components (`cpu/`)
#### Intel 4004 CPU
- **File**: `intel_4004.rs`
- **Features**:
- Complete Intel 4004 instruction set implementation
- 4-bit data processing
- 12-bit address space
- Two-phase clock operation (Φ1, Φ2)
- 16 general-purpose registers (8 index, 8 main)
- Stack-based subroutine calls
- Complete Intel 4004 instruction set implementation
- 4-bit data processing
- 12-bit address space
- Two-phase clock operation (Φ1, Φ2)
- 16 general-purpose registers (8 index, 8 main)
- Stack-based subroutine calls
- **Status**: Fully implemented with comprehensive testing
#### MOS 6502 CPU (Placeholder)
- **File**: `mos_6502.rs`
- **Features**: Placeholder for MOS Technology 6502 implementation
- **Status**: Not yet implemented
#### WDC 65C02 CPU (Placeholder)
- **File**: `wdc_65c02.rs`
- **Features**: Placeholder for Western Design Center 65C02 implementation
- **Status**: Not yet implemented
@ -39,67 +43,74 @@ All components implement the base `Component` trait defined in `src/component.rs
### Memory Components (`memory/`)
#### Intel 4001 ROM
- **File**: `intel_4001.rs`
- **Features**:
- 256 bytes of mask-programmable ROM
- 4-bit I/O ports for peripheral interface
- Two-phase addressing (8-bit address in two 4-bit cycles)
- 500ns typical access time
- SYNC signal detection for instruction fetch
- 256 bytes of mask-programmable ROM
- 4-bit I/O ports for peripheral interface
- Two-phase addressing (8-bit address in two 4-bit cycles)
- 500ns typical access time
- SYNC signal detection for instruction fetch
- **Status**: Fully implemented with comprehensive testing
#### Intel 4002 RAM
- **File**: `intel_4002.rs`
- **Features**:
- 320 bits of read/write memory (80 × 4-bit characters)
- 4-bit output port for status/display
- Banked memory organization (4 banks × 20 characters)
- Refresh circuitry for dynamic memory cells
- Status character instructions for control
- 320 bits of read/write memory (80 × 4-bit characters)
- 4-bit output port for status/display
- Banked memory organization (4 banks × 20 characters)
- Refresh circuitry for dynamic memory cells
- Status character instructions for control
- **Status**: Implemented with known limitations
#### Intel 4003 Shift Register
- **File**: `intel_4003.rs`
- **Features**:
- 10-bit static shift register
- Serial input/output capability
- Parallel input from data bus
- 200ns typical access time
- Bidirectional shifting
- 10-bit static shift register
- Serial input/output capability
- Parallel input from data bus
- 200ns typical access time
- Bidirectional shifting
- **Status**: Basic implementation
#### Generic Memory Components
- **GenericRAM**: `generic_ram.rs` - Configurable RAM implementation
- **GenericROM**: `generic_rom.rs` - Configurable ROM implementation
### Common Functionality (`common/`)
#### Intel 400x Series Common Code
- **File**: `intel_400x.rs`
- **Purpose**: Shared functionality for all Intel 400x series chips
- **Features**:
- Common timing constants and state machines
- Clock edge detection and handling
- Data bus operations and bit manipulation
- Address latching and assembly logic
- Control pin reading and management
- Reset handling and initialization
- Memory operation state machines
- Common timing constants and state machines
- Clock edge detection and handling
- Data bus operations and bit manipulation
- Address latching and assembly logic
- Control pin reading and management
- Reset handling and initialization
- Memory operation state machines
### Clock Components (`clock/`)
#### Generic Clock Generator
- **File**: `generic_clock.rs`
- **Features**:
- Configurable frequency and duty cycle
- Multiple output waveforms
- Synchronization capabilities
- Test pattern generation
- Configurable frequency and duty cycle
- Multiple output waveforms
- Synchronization capabilities
- Test pattern generation
- **Status**: Basic implementation
## Component Interface
### Required Methods
All components must implement:
```rust
@ -113,6 +124,7 @@ fn is_running(&self) -> bool; // Execution status
```
### Optional Traits
Components can implement additional traits for specialized functionality:
- **RunnableComponent**: Automatic thread spawning
@ -122,12 +134,14 @@ Components can implement additional traits for specialized functionality:
## Pin Configuration
### Standard Pin Types
- **Data Pins**: `D0-D3` for 4-bit data/address bus
- **Clock Pins**: `PHI1`, `PHI2` for two-phase clock
- **Control Pins**: `SYNC`, `RESET`, `CM`, `CI` for system control
- **I/O Pins**: `IO0-IO3` for peripheral interface
### Pin Signal States
- **High**: Logic level 1
- **Low**: Logic level 0
- **HighZ**: High impedance (tri-state, not driving)
@ -135,6 +149,7 @@ Components can implement additional traits for specialized functionality:
## Usage Examples
### Creating and Using a Component
```rust
use rusty_emu::components::memory::intel_4001::Intel4001;
@ -143,38 +158,42 @@ let mut rom = Intel4001::new("ROM1".to_string());
// Load program data
let program = vec![0x12, 0x34, 0x56, 0x78];
rom.load_rom_data(program, 0)?;
rom.load_rom_data(program, 0) ?;
// Use in simulation loop
while simulation_running() {
rom.update(); // Process one clock cycle
rom.update(); // Process one clock cycle
}
```
### Component Interconnection
```rust
// Components connect through shared pin references
let cpu_pin = cpu.get_pin("D0")?;
let rom_pin = rom.get_pin("D0")?;
let ram_pin = ram.get_pin("D0")?;
let cpu_pin = cpu.get_pin("D0") ?;
let rom_pin = rom.get_pin("D0") ?;
let ram_pin = ram.get_pin("D0") ?;
// Connect pins together
connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
connect_pins(vec![cpu_pin, rom_pin, ram_pin]) ?;
```
## Testing Strategy
### Unit Tests
- Located in each component's implementation file
- Test individual component behavior in isolation
- Focus on state management and pin operations
### Integration Tests
- Located in `tests/` directory
- Test component interactions and system behavior
- Verify proper timing and signal propagation
### Mock-Based Tests
- Use mock components for controlled testing
- Test error conditions and edge cases
- Verify behavior with various input patterns
@ -182,6 +201,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Development Guidelines
### Adding New Components
1. **Implement Core Traits**: Start with `Component` trait implementation
2. **Define Pin Configuration**: Specify required pins and their purposes
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
### Component Design Principles
- **Single Responsibility**: Each component should have one primary function
- **Clear Interfaces**: Well-defined pin configurations and behaviors
- **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
### Performance Considerations
- **Pin Access**: Minimize lock contention on shared pins
- **State Updates**: Efficient state machine transitions
- **Memory Layout**: Optimize memory access patterns
@ -205,6 +227,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Hardware Accuracy
### Intel 4004 Series Specifics
- **Two-Phase Clock**: All operations synchronized to Φ1 and Φ2
- **4-Bit Architecture**: Data processed in 4-bit nibbles
- **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
### Implementation Notes
- Some timing specifications are approximated due to Rust's timing limitations
- Pin signal propagation is simplified for simulation performance
- Bus contention handling prioritizes correctness over perfect hardware accuracy
@ -220,6 +244,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
## Future Enhancements
### Planned Components
- Enhanced Intel 4002 RAM with proper refresh cycles
- Intel 4008/4009 I/O expanders
- Intel 4269 programmable ROM
@ -227,6 +252,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
- Cycle-accurate instruction execution
### Architecture Improvements
- Better pin signal propagation modeling
- More sophisticated bus arbitration
- Enhanced timing constraint checking

View file

@ -1,10 +1,12 @@
# 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
The Intel 400x series shares many common characteristics:
- **Two-phase clock operation** (Φ1, Φ2)
- **4-bit data bus** with multiplexed address/data
- **12-bit address space** (8-bit addresses in two 4-bit cycles)
@ -61,6 +63,7 @@ pub trait Intel400xClockHandling {
```
**Key Features:**
- Edge detection for both clock phases
- Previous state tracking for transition detection
- Thread-safe pin access
@ -81,6 +84,7 @@ pub trait Intel400xDataBus {
```
**Key Features:**
- Safe pin access with error handling
- Driver name tracking for debugging
- Bus contention avoidance
@ -101,6 +105,7 @@ pub trait Intel400xAddressHandling {
```
**Key Features:**
- Two-phase address latching (high nibble, then low nibble)
- Timing-aware address assembly
- Access time validation
@ -121,6 +126,7 @@ pub trait Intel400xControlPins {
```
**Key Features:**
- Safe pin reading with fallback values
- Consistent pin naming across components
@ -139,6 +145,7 @@ pub trait Intel400xResetHandling {
```
**Key Features:**
- System-wide reset signal handling
- Component-specific reset behaviors
@ -167,6 +174,7 @@ pub trait Intel400xTimingState {
```
**Key Features:**
- Unified state machine interface
- Timing constraint management
- Address tracking and validation
@ -243,6 +251,7 @@ pub mod utils {
### Error Handling
All pin operations include proper error handling:
- **Lock failures**: Return safe default values
- **Pin not found**: Return appropriate error messages
- **Invalid states**: Graceful degradation
@ -347,19 +356,19 @@ The common code implements hardware-accurate timing where possible:
### Planned Improvements
1. **Enhanced Timing Simulation**
- More accurate sub-nanosecond timing
- Better clock phase relationship modeling
- Improved signal propagation delays
- More accurate sub-nanosecond timing
- Better clock phase relationship modeling
- Improved signal propagation delays
2. **Extended Component Support**
- Support for additional Intel 400x variants
- Enhanced I/O device support
- Better peripheral modeling
- Support for additional Intel 400x variants
- Enhanced I/O device support
- Better peripheral modeling
3. **Testing Improvements**
- More sophisticated mock scenarios
- Better timing verification tools
- Enhanced debugging capabilities
- More sophisticated mock scenarios
- Better timing verification tools
- Enhanced debugging capabilities
### Extension Points
@ -394,4 +403,5 @@ The common functionality depends on:
- **Shared Code**: No duplication across components
- **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
//! 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::pin::{Pin, PinValue};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
// Memory operation state machine states (shared across all 400x chips)
#[derive(Debug, Clone, Copy, PartialEq)]
@ -17,15 +17,14 @@ pub enum MemoryState {
DriveData, // Latency elapsed, driving data on bus
}
/// Common timing constants for Intel 400x series
pub struct TimingConstants;
impl TimingConstants {
pub const DEFAULT_ACCESS_TIME: Duration = Duration::from_nanos(500); // 500ns default
pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers
pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time
pub const DATA_VALID: Duration = Duration::from_nanos(200); // Data valid delay
pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers
pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time
pub const DATA_VALID: Duration = Duration::from_nanos(200); // Data valid delay
}
/// Common clock edge detection and timing functionality
@ -121,7 +120,10 @@ pub trait Intel400xDataBus {
} else {
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 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
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
/// Hardware: Intel 4004 provides address in two 4-bit phases
/// 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) {
Some(((high as u16) << 4) | (low as u16))
} else {
@ -163,7 +172,7 @@ pub trait Intel400xAddressHandling {
address_low_nibble: &mut Option<u8>,
full_address_ready: &mut bool,
address_latch_time: &mut Option<Instant>,
access_time: Duration,
_access_time: Duration,
) {
if address_high_nibble.is_none() {
// First cycle: latch high nibble (bits 7-4)
@ -173,7 +182,9 @@ pub trait Intel400xAddressHandling {
*address_low_nibble = Some(nibble);
// 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;
*address_latch_time = Some(Instant::now());
@ -186,7 +197,11 @@ pub trait Intel400xAddressHandling {
/// Handle latency timing during wait state
/// 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 {
latch_time.elapsed() >= access_time
} else {
@ -214,7 +229,8 @@ pub trait Intel400xControlPins {
/// Read CM-ROM pin state
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() {
pin_guard.read() == PinValue::High
} else {
@ -413,4 +429,4 @@ pub mod utils {
}
// Re-export commonly used items for convenience
pub use utils::*;
pub use utils::*;

View file

@ -3,4 +3,4 @@
pub mod intel_400x;
// Re-export commonly used items for convenience
pub use intel_400x::*;
pub use intel_400x::*;

View file

@ -40,26 +40,26 @@ enum MemoryState {
#[derive(Debug, Clone, Copy, PartialEq)]
enum Instruction {
// Data Transfer Instructions (8)
Ldm(u8), // Load accumulator immediate (LDM #)
Ld(u8), // Load accumulator from register (LD R)
Xch(u8), // Exchange accumulator with register (XCH R)
Add(u8), // Add register to accumulator (ADD R)
Sub(u8), // Subtract register from accumulator (SUB R)
Inc(u8), // Increment register (INC R)
Bbl(u8), // Branch back and load (BBL #)
Jun(u16), // Jump unconditional (JUN addr)
Ldm(u8), // Load accumulator immediate (LDM #)
Ld(u8), // Load accumulator from register (LD R)
Xch(u8), // Exchange accumulator with register (XCH R)
Add(u8), // Add register to accumulator (ADD R)
Sub(u8), // Subtract register from accumulator (SUB R)
Inc(u8), // Increment register (INC R)
Bbl(u8), // Branch back and load (BBL #)
Jun(u16), // Jump unconditional (JUN addr)
// Arithmetic Instructions (4)
AddC(u8), // Add register with carry (ADC R)
SubC(u8), // Subtract register with carry (SBC R)
Dad(u8), // Decimal add register (DAD R)
Daa, // Decimal adjust accumulator (DAA)
AddC(u8), // Add register with carry (ADC R)
SubC(u8), // Subtract register with carry (SBC R)
Dad(u8), // Decimal add register (DAD R)
Daa, // Decimal adjust accumulator (DAA)
// Logic Instructions (4)
Ral, // Rotate left (RAL)
Rar, // Rotate right (RAR)
Tcc, // Transmit carry clear (TCC)
Tcs, // Transmit carry set (TCS)
Ral, // Rotate left (RAL)
Rar, // Rotate right (RAR)
Tcc, // Transmit carry clear (TCC)
Tcs, // Transmit carry set (TCS)
// Control Transfer Instructions (8)
Jcn(u8, u16), // Jump conditional (JCN condition, addr)
@ -68,25 +68,25 @@ enum Instruction {
JntInvert(u16), // Jump on test inverted (JNT addr) - wait instruction
// Machine Instruction (1)
Src(u8), // Send register control (SRC R)
Src(u8), // Send register control (SRC R)
// Input/Output and RAM Instructions (8)
Wrm, // Write accumulator to RAM (WRM)
Wmp, // Write memory pointer (WMP)
Wrr, // Write ROM port and register (WRR)
Wpm, // Write program memory (WPM)
Adm, // Add from memory (ADM)
Sbm, // Subtract from memory (SBM)
Rdm, // Read memory (RDM)
Rdr, // Read ROM port and register (RDR)
Wrm, // Write accumulator to RAM (WRM)
Wmp, // Write memory pointer (WMP)
Wrr, // Write ROM port and register (WRR)
Wpm, // Write program memory (WPM)
Adm, // Add from memory (ADM)
Sbm, // Subtract from memory (SBM)
Rdm, // Read memory (RDM)
Rdr, // Read ROM port and register (RDR)
// Accumulator Group Instructions (8)
Clb, // Clear both (CLB)
Clc, // Clear carry (CLC)
Cmc, // Complement carry (CMC)
Stc, // Set carry (STC)
Cma, // Complement accumulator (CMA)
Iac, // Increment accumulator (IAC)
Clb, // Clear both (CLB)
Clc, // Clear carry (CLC)
Cmc, // Complement carry (CMC)
Stc, // Set carry (STC)
Cma, // Complement accumulator (CMA)
Iac, // Increment accumulator (IAC)
// Note: CMC and RAL are already defined above
// Invalid instruction
@ -124,19 +124,19 @@ pub struct Intel4004 {
ram_bank: u8, // Currently selected RAM bank (0-7)
// Two-phase clock state tracking
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
// Memory operation state machine
memory_state: MemoryState, // Current state of memory operation
address_high_nibble: Option<u8>, // High nibble of 8-bit address
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
full_address_ready: bool, // Whether complete address is assembled
memory_state: MemoryState, // Current state of memory operation
address_high_nibble: Option<u8>, // High nibble of 8-bit address
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
full_address_ready: bool, // Whether complete address is assembled
// Instruction execution state
current_op: Instruction, // Currently decoded instruction
operand_latch: u8, // Latched operand for multi-byte instructions
jump_address: Option<u16>, // Jump target address for conditional jumps
current_op: Instruction, // Currently decoded instruction
operand_latch: u8, // Latched operand for multi-byte instructions
jump_address: Option<u16>, // Jump target address for conditional jumps
// Timing and synchronization
address_latch_time: Option<Instant>, // Timestamp when address was latched
@ -150,13 +150,13 @@ impl Intel4004 {
pub fn new(name: String, clock_speed: f64) -> Self {
let pin_names = vec![
"D0", "D1", "D2", "D3", // Data bus pins
"SYNC", // Sync signal
"CM_ROM", // ROM chip select
"CM_RAM", // RAM chip select
"TEST", // Test pin
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
"SYNC", // Sync signal
"CM_ROM", // ROM chip select
"CM_RAM", // RAM chip select
"TEST", // Test pin
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
];
let pins = BaseComponent::create_pin_map(&pin_names, &name);
@ -462,7 +462,8 @@ impl Intel4004 {
/// Hardware: RESET pin clears all internal state and tri-states outputs
fn handle_reset(&mut self) {
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
self.accumulator = 0;
self.carry = false;
@ -575,7 +576,7 @@ impl Intel4004 {
fn assemble_full_address(&mut self) {
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
// 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.address_latch_time = Some(Instant::now());
@ -646,7 +647,7 @@ impl Intel4004 {
0x00..=0x0F => {
let reg = opcode & 0x0F;
if opcode < 0x08 {
Instruction::Ld(reg) // LD R
Instruction::Ld(reg) // LD R
} else {
Instruction::Xch(reg) // XCH R
}
@ -656,17 +657,17 @@ impl Intel4004 {
0x10..=0x1F => {
let reg = opcode & 0x0F;
if opcode < 0x18 {
Instruction::Add(reg) // ADD R
Instruction::Add(reg) // ADD R
} else {
Instruction::Sub(reg) // SUB R
Instruction::Sub(reg) // SUB R
}
}
// Logic Instructions (0x20-0x2F)
0x20..=0x2F => {
match opcode {
0x20..=0x27 => Instruction::AddC(opcode & 0x0F), // ADC R
0x28..=0x2F => Instruction::SubC(opcode & 0x0F), // SBC R
0x20..=0x27 => Instruction::AddC(opcode & 0x0F), // ADC R
0x28..=0x2F => Instruction::SubC(opcode & 0x0F), // SBC R
_ => Instruction::Invalid,
}
}
@ -674,10 +675,10 @@ impl Intel4004 {
// Control Transfer Instructions (0x30-0x3F)
0x30..=0x3F => {
match opcode {
0x30..=0x33 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x34..=0x37 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x38..=0x3B => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x3C..=0x3F => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x30..=0x33 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x34..=0x37 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x38..=0x3B => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
0x3C..=0x3F => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
_ => Instruction::Invalid,
}
}
@ -685,24 +686,23 @@ impl Intel4004 {
// Immediate Instructions (0x40-0x4F)
0x40..=0x4F => {
let reg = opcode & 0x0F;
Instruction::Ldm(reg) // LDM #
Instruction::Ldm(reg) // LDM #
}
// I/O and RAM Instructions (0x50-0x5F)
0x50..=0x5F => {
match opcode {
0x50..=0x57 => Instruction::Wrm, // WRM
0x58..=0x5F => Instruction::Wmp, // WMP
0x50..=0x57 => Instruction::Wrm, // WRM
0x58..=0x5F => Instruction::Wmp, // WMP
_ => Instruction::Invalid,
}
}
// Register Reference Instructions (0x60-0x6F)
0x60..=0x6F => {
let reg = opcode & 0x0F;
match opcode {
0x60..=0x67 => Instruction::Wrr, // WRR
0x68..=0x6F => Instruction::Wpm, // WPM
0x60..=0x67 => Instruction::Wrr, // WRR
0x68..=0x6F => Instruction::Wpm, // WPM
_ => Instruction::Invalid,
}
}
@ -710,22 +710,22 @@ impl Intel4004 {
// Accumulator Group Instructions (0x70-0x7F)
0x70..=0x7F => {
match opcode {
0x70 => Instruction::Adm, // ADM
0x71 => Instruction::Sbm, // SBM
0x72 => Instruction::Clb, // CLB
0x73 => Instruction::Clc, // CLC
0x74 => Instruction::Cmc, // CMC
0x75 => Instruction::Stc, // STC
0x76 => Instruction::Cma, // CMA
0x77 => Instruction::Iac, // IAC
0x78 => Instruction::Rdm, // RDM
0x79 => Instruction::Rdr, // RDR
0x7A => Instruction::Ral, // RAL
0x7B => Instruction::Rar, // RAR
0x7C => Instruction::Tcc, // TCC
0x7D => Instruction::Tcs, // TCS
0x7E => Instruction::Daa, // DAA
0x7F => Instruction::Tcs, // TCS (duplicate in some docs)
0x70 => Instruction::Adm, // ADM
0x71 => Instruction::Sbm, // SBM
0x72 => Instruction::Clb, // CLB
0x73 => Instruction::Clc, // CLC
0x74 => Instruction::Cmc, // CMC
0x75 => Instruction::Stc, // STC
0x76 => Instruction::Cma, // CMA
0x77 => Instruction::Iac, // IAC
0x78 => Instruction::Rdm, // RDM
0x79 => Instruction::Rdr, // RDR
0x7A => Instruction::Ral, // RAL
0x7B => Instruction::Rar, // RAR
0x7C => Instruction::Tcc, // TCC
0x7D => Instruction::Tcs, // TCS
0x7E => Instruction::Daa, // DAA
0x7F => Instruction::Tcs, // TCS (duplicate in some docs)
_ => Instruction::Invalid,
}
}
@ -733,62 +733,62 @@ impl Intel4004 {
// Jump Instructions (0x80-0x8F)
0x80..=0x8F => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R
Instruction::Src(reg) // SRC R
}
// Jump Instructions (0x90-0x9F)
0x90..=0x9F => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R (continued)
Instruction::Src(reg) // SRC R (continued)
}
// Jump Instructions (0xA0-0xAF)
0xA0..=0xAF => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R (continued)
Instruction::Src(reg) // SRC R (continued)
}
// Jump Instructions (0xB0-0xBF)
0xB0..=0xBF => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R (continued)
Instruction::Src(reg) // SRC R (continued)
}
// Jump Instructions (0xC0-0xCF)
0xC0..=0xCF => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R (continued)
Instruction::Src(reg) // SRC R (continued)
}
// Jump Instructions (0xD0-0xDF)
0xD0..=0xDF => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R (continued)
Instruction::Src(reg) // SRC R (continued)
}
// Jump Instructions (0xE0-0xEF)
0xE0..=0xEF => {
let reg = opcode & 0x0F;
Instruction::Src(reg) // SRC R (continued)
Instruction::Src(reg) // SRC R (continued)
}
// Jump Instructions (0xF0-0xFF)
0xF0..=0xFF => {
match opcode {
0xF0 => Instruction::Clb, // CLB
0xF1 => Instruction::Clc, // CLC
0xF2 => Instruction::Iac, // IAC
0xF3 => Instruction::Cmc, // CMC
0xF4 => Instruction::Cma, // CMA
0xF5 => Instruction::Ral, // RAL
0xF6 => Instruction::Rar, // RAR
0xF7 => Instruction::Rar, // RAR (duplicate)
0xF8 => Instruction::Daa, // DAA
0xF9 => Instruction::Daa, // DAA (duplicate)
0xFA => Instruction::Stc, // STC
0xFB => Instruction::Stc, // STC (duplicate)
0xFC => Instruction::Tcc, // TCC
0xFD => Instruction::Tcs, // TCS
0xF0 => Instruction::Clb, // CLB
0xF1 => Instruction::Clc, // CLC
0xF2 => Instruction::Iac, // IAC
0xF3 => Instruction::Cmc, // CMC
0xF4 => Instruction::Cma, // CMA
0xF5 => Instruction::Ral, // RAL
0xF6 => Instruction::Rar, // RAR
0xF7 => Instruction::Rar, // RAR (duplicate)
0xF8 => Instruction::Daa, // DAA
0xF9 => Instruction::Daa, // DAA (duplicate)
0xFA => Instruction::Stc, // STC
0xFB => Instruction::Stc, // STC (duplicate)
0xFC => Instruction::Tcc, // TCC
0xFD => Instruction::Tcs, // TCS
0xFE => Instruction::Invalid,
0xFF => Instruction::Invalid,
_ => Instruction::Invalid,
@ -839,7 +839,9 @@ impl Intel4004 {
Instruction::Sub(reg) => {
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.accumulator = result & 0x0F;
}
@ -860,8 +862,12 @@ impl Intel4004 {
Instruction::SubC(reg) => {
if reg < 16 {
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);
self.carry = self.accumulator < (self.index_registers[reg as usize] + carry_val);
let result = self
.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.program_counter.inc();
@ -870,14 +876,16 @@ impl Intel4004 {
// Logic Instructions
Instruction::Ral => {
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.program_counter.inc();
}
Instruction::Rar => {
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.program_counter.inc();
}
@ -947,22 +955,22 @@ impl Intel4004 {
Instruction::Jcn(condition, addr) => {
let should_jump = match condition {
0x0 => !self.carry, // JNT (Jump if no carry)
0x1 => self.carry, // JC (Jump if carry)
0x2 => self.accumulator == 0, // JZ (Jump if zero)
0x3 => self.accumulator != 0, // JNZ (Jump if not zero)
0x4 => true, // JUN (Jump unconditional)
0x5 => false, // Always false
0x6 => true, // Always true
0x7 => false, // Always false
0x8 => true, // Always true
0x9 => false, // Always false
0xA => true, // Always true
0xB => false, // Always false
0xC => true, // Always true
0xD => false, // Always false
0xE => true, // Always true
0xF => false, // Always false
0x0 => !self.carry, // JNT (Jump if no carry)
0x1 => self.carry, // JC (Jump if carry)
0x2 => self.accumulator == 0, // JZ (Jump if zero)
0x3 => self.accumulator != 0, // JNZ (Jump if not zero)
0x4 => true, // JUN (Jump unconditional)
0x5 => false, // Always false
0x6 => true, // Always true
0x7 => false, // Always false
0x8 => true, // Always true
0x9 => false, // Always false
0xA => true, // Always true
0xB => false, // Always false
0xC => true, // Always true
0xD => false, // Always false
0xE => true, // Always true
0xF => false, // Always false
_ => false,
};
@ -1042,7 +1050,8 @@ impl Intel4004 {
// Increment Register Instructions
Instruction::Inc(reg) => {
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();
}

View file

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

View file

@ -4,12 +4,12 @@ use std::thread;
use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component, RunnableComponent};
use crate::pin::{Pin, PinValue};
use crate::components::common::intel_400x::{
Intel400xClockHandling, Intel400xDataBus, Intel400xAddressHandling,
Intel400xControlPins, Intel400xResetHandling, Intel400xTimingState,
TimingState, MemoryState, RamState, utils
Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins,
Intel400xDataBus, Intel400xResetHandling, Intel400xTimingState, RamState,
TimingState,
};
use crate::pin::{Pin, PinValue};
/// 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
@ -28,39 +28,38 @@ use crate::components::common::intel_400x::{
/// - Bank selection and status character handling follows Intel MCS-4 architecture
pub struct Intel4002 {
base: BaseComponent,
variant: RamVariant, // RAM variant (4002-1 or 4002-2)
memory: [u8; 80], // 80 nibbles of RAM (320 bits total) - 4 banks × 20 nibbles
last_address: u8, // Last accessed memory address
access_time: Duration, // RAM access latency (500ns typical)
variant: RamVariant, // RAM variant (4002-1 or 4002-2)
memory: [u8; 80], // 80 nibbles of RAM (320 bits total) - 4 banks × 20 nibbles
last_address: u8, // Last accessed memory address
access_time: Duration, // RAM access latency (500ns typical)
address_latch_time: Option<Instant>, // Timestamp when address was latched
output_ports: [u8; 4], // 4 output ports (4 bits each) - TODO: Make [[u8; 4]; 4] for 4-bit ports
input_latch: u8, // Input data latch for I/O operations
status_characters: [u8; 4], // 4 separate status character latches (4 bits each)
bank_select: u8, // RAM bank selection (2 bits)
output_ports: [u8; 4], // 4 output ports (4 bits each) - TODO: Make [[u8; 4]; 4] for 4-bit ports
input_latch: u8, // Input data latch for I/O operations
status_characters: [u8; 4], // 4 separate status character latches (4 bits each)
bank_select: u8, // RAM bank selection (2 bits)
// Clock edge detection (same as 4001)
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
// Two-phase addressing for 8-bit address (same as 4001)
address_high_nibble: Option<u8>, // High nibble of 8-bit address
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
full_address_ready: bool, // Whether complete address is assembled
// RAM operation state machine
ram_state: RamState, // Current state of RAM operation
ram_state: RamState, // Current state of RAM operation
// Data latching for RAM operations
data_latch: Option<u8>, // Latched data for write operations
data_latch: Option<u8>, // Latched data for write operations
// Instruction cycle tracking
instruction_phase: bool, // Whether we're in instruction phase
current_instruction: u8, // Current instruction being processed
instruction_phase: bool, // Whether we're in instruction phase
current_instruction: u8, // Current instruction being processed
}
/// Intel 4002 RAM variants
/// - 4002-1: Standard RAM variant
/// - 4002-2: RAM variant with different configuration
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RamVariant {
Type1, // 4002-1
Type2, // 4002-2
Type1, // 4002-1
Type2, // 4002-2
}
/// Intel 4002 timing constants (based on datasheet specifications)
@ -68,10 +67,10 @@ pub enum RamVariant {
struct TimingConstants;
impl TimingConstants {
const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // T_ADDRESS_SETUP
const DATA_VALID: Duration = Duration::from_nanos(200); // T_DATA_VALID
const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // T_ADDRESS_SETUP
const DATA_VALID: Duration = Duration::from_nanos(200); // T_DATA_VALID
const OUTPUT_DISABLE: Duration = Duration::from_nanos(150); // T_OUTPUT_DISABLE
const RAM_ACCESS: Duration = Duration::from_nanos(500); // RAM access time
const RAM_ACCESS: Duration = Duration::from_nanos(500); // RAM access time
}
impl Intel400xClockHandling for Intel4002 {
@ -163,7 +162,7 @@ impl Intel4002 {
Self::new_with_variant_and_access_time(name, RamVariant::Type1, 500) // Default 500ns access time
}
/// Create a new Intel 4002 RAM with custom access time (for testing)
/// Create a new Intel 4002 RAM with custom access time (for testing)
/// Parameters: name - Component identifier, access_time_ns - Memory access time in nanoseconds
/// Returns: New Intel4002 instance with configurable access timing
pub fn new_with_access_time(name: String, access_time_ns: u64) -> Self {
@ -173,7 +172,11 @@ impl Intel4002 {
/// 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
/// 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):
// - 4 data pins (D0-D3) for multiplexed address/data
// - 4 output port pins (O0-O3)
@ -186,28 +189,28 @@ impl Intel4002 {
// - P0: RAM chip select (must be HIGH for RAM access)
// - RESET: Clears internal state
let pin_names = vec![
"D0", "D1", "D2", "D3", // Data/Address pins
"D0", "D1", "D2", "D3", // Data/Address pins
"O0", "O1", "O2", "O3", // Output port pins
"SYNC", // Sync signal
"CM", // ROM Chip Select
"P0", // RAM Chip Select
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
"SYNC", // Sync signal
"CM", // ROM Chip Select
"P0", // RAM Chip Select
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
];
let pins = BaseComponent::create_pin_map(&pin_names, &name);
Intel4002 {
Intel4002 {
base: BaseComponent::new(name, pins),
variant,
memory: [0u8; 80], // 80 nibbles = 4 banks × 20 nibbles each
memory: [0u8; 80], // 80 nibbles = 4 banks × 20 nibbles each
last_address: 0,
access_time: Duration::from_nanos(access_time_ns),
address_latch_time: None,
output_ports: [0u8; 4],
input_latch: 0,
status_characters: [0u8; 4], // 4 separate status character latches
status_characters: [0u8; 4], // 4 separate status character latches
bank_select: 0,
prev_phi1: PinValue::Low,
prev_phi2: PinValue::Low,
@ -312,7 +315,8 @@ impl Intel4002 {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
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 {
if let Ok(pin) = self.base.get_pin(&format!("O{}", port)) {
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
/// Returns: (PHI1_value, PHI2_value)
/// Hardware: Intel 4004 provides two-phase clock for synchronization
@ -358,11 +364,6 @@ impl Intel4002 {
(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 {
let (phi1, _) = self.read_clock_pins();
phi1 == PinValue::Low && self.prev_phi1 == PinValue::High
@ -436,10 +437,10 @@ impl Intel4002 {
fn handle_reset(&mut self) {
if Intel400xResetHandling::handle_reset(self, "RESET") {
// Hardware reset - clear all registers specific to Intel4002
self.memory = [0u8; 80]; // Clear 80 nibbles
self.memory = [0u8; 80]; // Clear 80 nibbles
self.output_ports = [0u8; 4];
self.input_latch = 0;
self.status_characters = [0u8; 4]; // Clear 4 status character latches
self.status_characters = [0u8; 4]; // Clear 4 status character latches
self.bank_select = 0;
// Reset all state machines using common functionality
@ -464,7 +465,7 @@ impl Intel4002 {
fn assemble_full_address(&mut self) {
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
// 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.address_latch_time = Some(Instant::now());
@ -482,18 +483,18 @@ impl Intel4002 {
// The 4002 uses the high nibble of the address for bank selection
// and the low nibble for within-bank addressing
// Each bank has 20 nibbles (0-19), not 16
let bank = (address_low >> 4) & 0x03; // 2-bit bank from high nibble
let ram_address = (bank * 20) + (address_low & 0x0F); // 20 nibbles per bank
let bank = (address_low >> 4) & 0x03; // 2-bit bank from high nibble
let ram_address = (bank * 20) + (address_low & 0x0F); // 20 nibbles per bank
// Status characters are separate latches, not part of RAM
if self.is_status_character_instruction(instruction) {
// Status characters are separate from RAM - return special address
return (bank, 0xFF); // Special marker for status character access
return (bank, 0xFF); // Special marker for status character access
}
// Output ports are also separate from RAM (addresses 0x14-0x17)
if address_low >= 0x14 && address_low <= 0x17 {
return (bank, 0xFF); // Special marker for output port access
return (bank, 0xFF); // Special marker for output port access
}
(bank, ram_address)
@ -833,9 +834,9 @@ impl Intel4002 {
/// Handle instruction cycle synchronization
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
self.instruction_phase = true;
self.current_instruction = self.read_data_bus();
@ -1071,7 +1072,7 @@ impl Intel4002 {
/// Clear all RAM to zero
pub fn clear_ram(&mut self) {
self.memory = [0u8; 80]; // Clear 80 nibbles
self.memory = [0u8; 80]; // Clear 80 nibbles
}
/// Get all RAM data for a specific bank
@ -1079,7 +1080,7 @@ impl Intel4002 {
/// Returns: Vector of 20 nibbles for the bank (4 banks × 20 nibbles = 80 total)
pub fn get_ram_bank(&self, bank: u8) -> Vec<u8> {
let bank = (bank & 0x03) as usize;
let start = bank * 20; // 20 nibbles per bank
let start = bank * 20; // 20 nibbles per bank
let end = start + 20;
if end <= self.memory.len() {
@ -1094,9 +1095,16 @@ impl Intel4002 {
/// Debug function to log state transitions for troubleshooting
/// Parameters: test_name - Name of the test for context
pub fn debug_state_transitions(&self, test_name: &str) {
println!("{} - State: {:?}, Bank: {}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}",
test_name, self.ram_state, self.bank_select, self.address_high_nibble,
self.address_low_nibble, self.last_address, self.full_address_ready);
println!(
"{} - State: {:?}, Bank: {}, High: {:?}, Low: {:?}, Address: 0x{:x}, 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
@ -1104,12 +1112,6 @@ impl Intel4002 {
self.current_instruction = instruction;
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
@ -1182,7 +1184,7 @@ mod tests {
let mut ram = Intel4002::new("RAM_4002".to_string());
// Write test data to different banks
ram.write_ram(0, 0x01).unwrap(); // Bank 0, address 0
ram.write_ram(0, 0x01).unwrap(); // Bank 0, address 0
ram.write_ram(20, 0x02).unwrap(); // Bank 1, address 0
ram.write_ram(40, 0x03).unwrap(); // Bank 2, address 0
@ -1272,9 +1274,15 @@ mod tests {
}
// 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
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
// Should have transitioned to AddressPhase
@ -1294,9 +1302,15 @@ mod tests {
}
// 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
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
// Should have assembled full address and transitioned to WaitLatency
@ -1446,13 +1460,13 @@ mod tests {
assert_eq!(ram.read_ram(4).unwrap(), 0x05);
// Test overflow
let large_data = vec![0u8; 81]; // More than 80 nibbles
let large_data = vec![0u8; 81]; // More than 80 nibbles
assert!(ram.initialize_ram(&large_data).is_err());
}
#[test]
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
assert_eq!(ram.ram_state, RamState::Idle);
@ -1500,8 +1514,8 @@ mod tests {
// SRC instruction sets up output port addressing
ram.setup_instruction_test(0x25); // SRC with specific address
// Verify output port addressing is set up correctly
// This would require more complex setup in a real implementation
// Verify output port addressing is set up correctly
// This would require more complex setup in a real implementation
}
#[test]

View file

@ -21,14 +21,14 @@ use crate::pin::{Pin, PinValue};
/// - Output behavior matches 4001/4002 I/O persistence model
pub struct Intel4003 {
base: BaseComponent,
shift_register: [u8; 10], // 10-bit shift register (stored as 10 bytes for simplicity)
output_latch: [u8; 10], // 10-bit output latch for parallel output
serial_input: u8, // Serial input data (4-bit)
access_time: Duration, // Shift register access latency (200ns typical)
shift_register: [u8; 10], // 10-bit shift register (stored as 10 bytes for simplicity)
output_latch: [u8; 10], // 10-bit output latch for parallel output
serial_input: u8, // Serial input data (4-bit)
access_time: Duration, // Shift register access latency (200ns typical)
// Clock edge detection
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
// Two-phase addressing for 8-bit address
address_high_nibble: Option<u8>, // High nibble of 8-bit address
@ -36,7 +36,7 @@ pub struct Intel4003 {
full_address_ready: bool, // Whether complete address is assembled
// Shift register operation state machine
shift_state: ShiftState, // Current state of shift operation
shift_state: ShiftState, // Current state of shift operation
address_latch_time: Option<Instant>, // Timestamp when address was latched
}
@ -74,21 +74,21 @@ impl Intel4003 {
// - CM: Chip select (must be HIGH for shift register access)
// - RESET: Clears internal state
let pin_names = vec![
"D0", "D1", "D2", "D3", // Data/Address pins
"O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins
"SYNC", // Sync signal
"CM", // Chip Select
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
"D0", "D1", "D2", "D3", // Data/Address pins
"O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins
"SYNC", // Sync signal
"CM", // Chip Select
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
];
let pins = BaseComponent::create_pin_map(&pin_names, &name);
Intel4003 {
base: BaseComponent::new(name, pins),
shift_register: [0u8; 10], // 10-bit shift register
output_latch: [0u8; 10], // 10-bit output latch
shift_register: [0u8; 10], // 10-bit shift register
output_latch: [0u8; 10], // 10-bit output latch
serial_input: 0,
access_time: Duration::from_nanos(access_time_ns),
@ -167,7 +167,8 @@ impl Intel4003 {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
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 {
if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) {
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
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
self.full_address_ready = true;
self.address_latch_time = Some(Instant::now());
@ -400,29 +404,41 @@ impl Intel4003 {
if sync && chip_select && self.full_address_ready {
// Read serial input data from 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)
// For 4003, we shift in 4 bits at a time
// First, shift existing bits right by 4 positions
for j in (4..10).rev() {
self.shift_register[j] = self.shift_register[j-4];
self.shift_register[j] = self.shift_register[j - 4];
}
// Then insert the 4 new bits into positions 0-3
for i in 0..4 {
let bit = (serial_data >> i) & 1;
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
self.output_latch.copy_from_slice(&self.shift_register);
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();
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;
} else {
@ -436,7 +452,6 @@ impl Intel4003 {
// This state maintains the output until next operation
}
/// Get the current shift register value
/// Returns: 10-bit value as a tuple (high_byte, low_2_bits)
pub fn get_shift_register(&self) -> (u8, u8) {
@ -593,9 +608,9 @@ mod tests {
let mut d2_guard = d2_pin.lock().unwrap();
let mut d3_guard = d3_pin.lock().unwrap();
d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 0 = 1
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0
d2_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 2 = 1
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0
}
// Set control pins for shift operation
@ -613,7 +628,10 @@ mod tests {
// Verify shift operation occurred
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);
// The shift register should now contain the shifted data
@ -623,7 +641,7 @@ mod tests {
// high byte: 01010101 (0x55) - this is correct, the first 4 bits are 0101 (0x05)
// low byte: 00000001 (0x01) - this is the remaining 2 bits from the original pattern
assert_eq!(high, 0x55); // First 8 bits should be 01010101
assert_eq!(low, 0x01); // Lower 2 bits should be 01
assert_eq!(low, 0x01); // Lower 2 bits should be 01
}
#[test]
@ -670,4 +688,4 @@ mod tests {
assert_eq!(fast_sr.get_access_time(), 1);
assert_eq!(fast_sr.name(), "FAST_SHIFT");
}
}
}

View file

@ -3,7 +3,7 @@ pub mod component;
pub mod components;
pub mod connection;
pub mod pin;
pub mod systems;
pub mod system_config;
pub mod types;
// 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::fs;
use std::process;
@ -8,7 +8,7 @@ use std::time::{Duration, Instant};
fn main() {
// Parse command line arguments
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 i = 1;
@ -89,28 +89,46 @@ fn print_usage(program_name: &str) {
println!("Usage: {} [OPTIONS]", program_name);
println!();
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!(" -h, --help Show this help message");
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!(" {} -s mcs4 -f fibonacci.bin", program_name);
println!(" {} --system mcs4 --file myprogram.bin", program_name);
println!(" {} -s basic -f fibonacci.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> {
println!("DEBUG: Attempting to load program from: {}", filename);
match fs::read(filename) {
Ok(data) => {
println!("DEBUG: Successfully loaded {} bytes from {}", data.len(), filename);
println!(
"DEBUG: Successfully loaded {} bytes from {}",
data.len(),
filename
);
Ok(data)
}
Err(e) => {
// If file doesn't exist, try to use default program
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();
println!("DEBUG: Default program size: {} bytes", default_program.len());
println!(
"DEBUG: Default program size: {} bytes",
default_program.len()
);
Ok(default_program)
} else {
println!("DEBUG: Failed to read file {}: {}", filename, e);
@ -124,79 +142,67 @@ fn get_default_fibonacci_program() -> Vec<u8> {
// Fibonacci program for Intel 4004
// This implements a simple Fibonacci sequence generator
vec![
0x20, 0x00, // LDM 0 (Load accumulator with 0) - First Fibonacci number
0x10, 0x00, // LD 0 (Load accumulator from register 0)
0x50, // WRM (Write accumulator to RAM at current pointer)
0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number
0x10, 0x01, // LD 1 (Load accumulator from register 1)
0x50, // WRM (Write accumulator to RAM)
0x20, 0x00, // LDM 0 (Load accumulator with 0) - First Fibonacci number
0x10, 0x00, // LD 0 (Load accumulator from register 0)
0x50, // WRM (Write accumulator to RAM at current pointer)
0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number
0x10, 0x01, // LD 1 (Load accumulator from register 1)
0x50, // WRM (Write accumulator to RAM)
// Fibonacci calculation loop
0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter
0x76, // IAC (Increment accumulator)
0x00, 0x02, // LD 2 (Store back to register 2)
0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter
0x76, // IAC (Increment accumulator)
0x00, 0x02, // LD 2 (Store back to register 2)
// Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2)
0x00, 0x00, // LD 0 (Load F(n-2) into accumulator)
0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator)
0x50, // WRM (Store result to RAM)
0x00, 0x00, // LD 0 (Load F(n-2) into accumulator)
0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator)
0x50, // WRM (Store result to RAM)
// Update registers for next iteration
0x00, 0x01, // LD 1 (Load F(n-1) into accumulator)
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))
0x00, 0x01, // LD 1 (Load F(n-1) into accumulator)
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))
// Check if we've calculated enough numbers (8 iterations)
0x00, 0x02, // LD 2 (Load loop counter)
0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition
0x40, 0x0C, // JCN 12 (Jump back to loop start)
0x00, 0x02, // LD 2 (Load loop counter)
0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition
0x40, 0x0C, // JCN 12 (Jump back to loop start)
// 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 {
"mcs4" => {
let system = IntelMcs4::new();
// Split program between ROMs
let rom1_size = 256;
let rom1_data = if program_data.len() > rom1_size {
program_data[..rom1_size].to_vec()
} else {
program_data.to_vec()
};
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)
"mcs4" | "basic" => {
// Use the basic MCS-4 configuration
factory.create_from_json("configs/mcs4_basic.json")
.map_err(|e| format!("Failed to create basic MCS-4 system: {}", e))
}
"mcs4_max" | "max" | "fig1" => {
// 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 {
Err(format!("Unknown system type: {}. Use 'basic', 'max', or provide a JSON config file path.", system_type))
}
}
_ => Err(format!("Unknown system type: {}", system_type))
}
}
fn run_system_demo(system: IntelMcs4) {
fn run_system_demo(system: ConfigurableSystem) {
let start_time = Instant::now();
println!("Cycle | Accumulator | PC");
println!("------|-------------|------");
// Display system information
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
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;
while iteration < 20 {
thread::sleep(Duration::from_millis(200));
while iteration < 10 {
thread::sleep(Duration::from_millis(500));
if let Ok(system) = system_arc.lock() {
if let Ok(state) = system.get_cpu_state() {
println!(
"{:5} | {:11} | {:04X}",
state.cycle_count,
state.accumulator,
state.program_counter
);
iteration += 1;
if !system.is_running() {
break;
}
}
if iteration >= 15 {
iteration += 1;
if iteration >= 8 {
if let Ok(mut system) = system_arc.lock() {
system.stop();
}
@ -249,4 +250,4 @@ mod tests {
assert!(!program.is_empty());
println!("Fibonacci program size: {} bytes", program.len());
}
}
}

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,17 +1,20 @@
# 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
### Unit Tests (in implementation files)
- **Intel4001 unit tests**: Located in `src/components/memory/intel_4001.rs`
- Component creation and configuration
- Memory operations and data loading
- State management and transitions
- Error handling and edge cases
- Component creation and configuration
- Memory operations and data loading
- State management and transitions
- Error handling and edge cases
### Integration Tests (in tests/ directory)
1. **`intel_400x_tests.rs`** - Tests for the common intel_400x functionality
2. **`mocks.rs`** - Mock implementations for testing hardware interactions
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
#### 1. Common Functionality Tests (`intel_400x_tests.rs`)
- **Timing Constants**: Verify access times and setup requirements
- **Address Assembly**: Test nibble-to-address conversion logic
- **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
#### 2. Mock-Based Tests (`mock_based_tests.rs`)
- **Data Bus Operations**: Test read/write operations with controlled inputs
- **Clock Handling**: Test clock pin interactions and edge scenarios
- **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
#### 3. Property-Based Tests (`property_based_tests.rs`)
- **State Machine Invariants**: Verify state consistency properties
- **Type Conversions**: Test enum conversion roundtrips
- **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
#### 4. Integration Tests (`integration_tests.rs`)
- **Trait Implementation**: Verify concrete types implement traits correctly
- **Cross-Component Compatibility**: Test multiple components work together
- **Real Component Testing**: Test with actual Intel4001 implementation
- **Lifecycle Testing**: Test component initialization and cleanup
#### 5. Working Tests (`working_test.rs`)
- **Core Functionality**: Demonstrates that the intel_400x module is testable
- **Basic Operations**: Tests fundamental features that work correctly
- **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
### Run All Tests
```bash
cargo test
```
### Run Specific Test Categories
```bash
# Unit tests only
cargo test intel_400x_tests
@ -81,11 +91,13 @@ cargo test demo
```
### Run with Detailed Output
```bash
cargo test -- --nocapture
```
### Run Specific Test
```bash
cargo test test_name
```
@ -93,6 +105,7 @@ cargo test test_name
## Test Architecture
### Mock System
The test suite includes a comprehensive mock system that provides:
- **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
### Property-Based Testing
Uses `proptest` to verify:
- State machine invariants hold for all inputs
@ -109,6 +123,7 @@ Uses `proptest` to verify:
- Timing constraints are maintained
### Integration Testing
Tests verify that:
- Concrete implementations (like Intel4001) correctly use the common traits
@ -118,28 +133,33 @@ Tests verify that:
## Key Testing Areas
### 1. Timing and Clock Logic
- Clock edge detection accuracy
- Two-phase clock handling
- Access time simulation
- State machine timing
### 2. Address Handling
- Nibble assembly into full addresses
- Address latching during clock phases
- Address validation and bounds checking
### 3. Data Bus Operations
- 4-bit data bus read/write operations
- Bus tri-stating for contention avoidance
- Bit-level manipulation and verification
### 4. State Machine Verification
- State transition correctness
- State invariant preservation
- Reset behavior verification
- Error state handling
### 5. Hardware Abstraction
- Pin operation safety
- Driver management
- Hardware timing simulation

View file

@ -5,8 +5,6 @@
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001;
use test_utils::*;
use test_config::TestConfig;
#[cfg(test)]
mod test_demonstration {
@ -35,8 +33,13 @@ mod test_demonstration {
// Test 3: Test state machine
println!("\n3. Testing state machine...");
scenario.component.set_timing_state(TimingState::AddressPhase);
assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase);
scenario
.component
.set_timing_state(TimingState::AddressPhase);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::AddressPhase
);
assert!(scenario.component.get_timing_state().is_address_phase());
println!("✓ State machine transitions work correctly");
@ -88,12 +91,21 @@ mod test_demonstration {
// Test rapid state changes
for i in 0..10 {
fast_scenario.component.set_timing_state(TimingState::Idle);
fast_scenario.component.set_timing_state(TimingState::AddressPhase);
fast_scenario.component.set_timing_state(TimingState::WaitLatency);
fast_scenario.component.set_timing_state(TimingState::DriveData);
fast_scenario
.component
.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");
// Test data bus operations
@ -111,9 +123,8 @@ mod test_demonstration {
// Test graceful error handling
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(0x100), None); // Out of bounds
println!("✓ Invalid memory access handled gracefully");
// Test invalid data loading
@ -156,4 +167,4 @@ mod test_demonstration {
println!("=== Performance demo completed ===");
}
}
}

View file

@ -4,10 +4,10 @@
//! the intel_400x traits and that the common functionality works
//! as expected in real usage scenarios.
use rusty_emu::component::Component;
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001;
use rusty_emu::pin::PinValue;
use rusty_emu::component::Component;
use std::time::Duration;
#[cfg(test)]
@ -188,13 +188,23 @@ mod intel_4001_integration_tests {
#[test]
fn test_timing_constants_usage() {
// Test that timing constants are used appropriately
let fast_rom = Intel4001::new_with_access_time("FAST_ROM".to_string(),
TimingConstants::FAST_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);
let fast_rom = Intel4001::new_with_access_time(
"FAST_ROM".to_string(),
TimingConstants::FAST_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!(default_rom.get_access_time(), 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!(
default_rom.get_access_time(),
TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64
);
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0));
@ -273,12 +283,8 @@ mod intel_4001_integration_tests {
fn test_error_handling_integration() {
let mut rom = Intel4001::new("ROM_4001".to_string());
// Test that the component handles invalid operations gracefully
// This tests the robustness of the trait implementations
// Test reading from invalid ROM address
// Test reading from valid ROM address
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
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
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
let timing_state: &dyn Intel400xTimingState = &rom;
@ -482,7 +488,7 @@ mod cross_component_integration_tests {
fn test_timing_consistency_across_components() {
// Test that timing behavior is consistent across different components
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
assert_eq!(rom1.get_timing_state(), rom2.get_timing_state());
@ -499,4 +505,4 @@ mod cross_component_integration_tests {
assert_eq!(rom1.get_timing_state(), TimingState::AddressPhase);
assert_eq!(rom2.get_timing_state(), TimingState::AddressPhase);
}
}
}

View file

@ -1,14 +1,14 @@
use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use rusty_emu::component::{BaseComponent, Component};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use std::collections::HashMap;
// Mock component for testing trait implementations
#[derive(Debug)]
struct MockComponent {
pins: std::collections::HashMap<String, Arc<Mutex<TestPin>>>,
pins: HashMap<String, Arc<Mutex<TestPin>>>,
}
#[derive(Debug)]
@ -40,7 +40,7 @@ impl Pin {
impl MockComponent {
fn new() -> 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> {
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]
fn test_timing_constants() {
// 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::ADDRESS_SETUP, Duration::from_nanos(100));
assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200));
@ -105,8 +111,6 @@ mod tests {
#[test]
fn test_assemble_full_address() {
let mock = MockComponent::new();
// Create a mock trait implementation for testing
struct TestAddressHandler {
base: MockComponent,
@ -129,10 +133,22 @@ mod tests {
let handler = TestAddressHandler::new();
// Test address assembly with valid nibbles
assert_eq!(handler.assemble_full_address(Some(0x0F), Some(0x23)), Some(0x0F23));
assert_eq!(handler.assemble_full_address(Some(0x12), Some(0x34)), Some(0x1234));
assert_eq!(handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x0000));
assert_eq!(handler.assemble_full_address(Some(0xFF), Some(0xFF)), Some(0xFFFF));
assert_eq!(
handler.assemble_full_address(Some(0x0F), Some(0x23)),
Some(0x0F23)
);
assert_eq!(
handler.assemble_full_address(Some(0x12), Some(0x34)),
Some(0x1234)
);
assert_eq!(
handler.assemble_full_address(Some(0x00), Some(0x00)),
Some(0x0000)
);
assert_eq!(
handler.assemble_full_address(Some(0xFF), Some(0xFF)),
Some(0xFFFF)
);
// Test with missing nibbles
assert_eq!(handler.assemble_full_address(None, Some(0x23)), None);
@ -216,9 +232,9 @@ mod tests {
#[test]
fn test_utils_create_driver_name() {
assert_eq!(utils::create_driver_name("COMP1", "DATA"), "COMP1_DATA");
assert_eq!(utils::create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR");
assert_eq!(utils::create_driver_name("CPU", "CONTROL"), "CPU_CONTROL");
assert_eq!(create_driver_name("COMP1", "DATA"), "COMP1_DATA");
assert_eq!(create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR");
assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL");
}
#[test]
@ -329,7 +345,7 @@ mod tests {
);
assert_eq!(high_nibble, None); // Should be cleared
assert_eq!(low_nibble, None); // Should be cleared
assert_eq!(low_nibble, None); // Should be cleared
assert_eq!(full_address_ready, true);
assert!(address_latch_time.is_some());
@ -366,4 +382,4 @@ mod tests {
// Note: Testing with elapsed time would require sleeping or mocking time
// This demonstrates the logic structure is correct
}
}
}

View file

@ -9,11 +9,11 @@
pub use rusty_emu;
// Module declarations for test files
mod intel_400x_tests;
mod mocks;
mod mock_based_tests;
mod property_based_tests;
mod integration_tests;
mod intel_400x_tests;
mod mock_based_tests;
mod mocks;
mod property_based_tests;
// Common test utilities and helpers
pub mod test_utils {
@ -36,10 +36,16 @@ pub mod test_utils {
/// Create a test scenario with known good values
pub fn create_standard_test_scenario() -> crate::mocks::MockScenario {
let mut scenario = crate::mocks::MockScenario::new("StandardTest");
scenario.component.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);
let scenario = crate::mocks::MockScenario::new("StandardTest");
scenario
.component
.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
}
@ -165,4 +171,4 @@ pub mod test_config {
self
}
}
}
}

View file

@ -5,7 +5,6 @@
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use crate::mocks::*;
use std::time::Duration;
#[cfg(test)]
@ -21,10 +20,10 @@ mod data_bus_tests {
assert_eq!(scenario.get_data_bus_value(), 0x05);
// Verify individual bits
assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::High)); // LSB
assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::High)); // LSB
assert_eq!(scenario.component.get_pin_value("D1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("D2"), Some(PinValue::High));
assert_eq!(scenario.component.get_pin_value("D3"), Some(PinValue::Low)); // MSB
assert_eq!(scenario.component.get_pin_value("D3"), Some(PinValue::Low)); // MSB
// Test writing different value
scenario.set_data_bus_value(0x0A); // 1010 in binary
@ -72,12 +71,20 @@ mod clock_handling_tests {
let scenario = MockScenario::new("TestClockReading");
// 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
// For now, we verify the mock setup works correctly
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
}
#[test]
@ -86,23 +93,47 @@ mod clock_handling_tests {
// Test PHI1 rising edge
scenario.set_phi1_rising_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::High));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::High)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
// Test PHI1 falling edge
scenario.set_phi1_falling_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
// Test PHI2 rising edge
scenario.set_phi2_rising_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::High));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::High)
);
// Test PHI2 falling edge
scenario.set_phi2_falling_edge();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
}
#[test]
@ -111,16 +142,34 @@ mod clock_handling_tests {
// Simulate a clock cycle: Low -> High -> Low
scenario.set_clock_low();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
scenario.set_clock_high();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::High));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::High));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::High)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::High)
);
scenario.set_clock_low();
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low));
assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low));
assert_eq!(
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
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);
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
scenario.component.set_pin_value("CM", PinValue::High);
@ -148,10 +203,16 @@ mod control_pin_tests {
// Test RESET pin
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);
assert_eq!(scenario.component.get_pin_value("RESET"), Some(PinValue::Low));
assert_eq!(
scenario.component.get_pin_value("RESET"),
Some(PinValue::Low)
);
}
#[test]
@ -166,7 +227,10 @@ mod control_pin_tests {
scenario.set_data_bus_value(0x0F);
// 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_address_high_nibble(), Some(0x12));
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)
for i in 0..4 {
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());
// Transition to address phase
scenario.component.set_timing_state(TimingState::AddressPhase);
assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase);
scenario
.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_address_phase());
// Transition to wait latency
scenario.component.set_timing_state(TimingState::WaitLatency);
assert_eq!(scenario.component.get_timing_state(), TimingState::WaitLatency);
scenario
.component
.set_timing_state(TimingState::WaitLatency);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::WaitLatency
);
assert!(scenario.component.get_timing_state().is_waiting_latency());
// Transition to drive data
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());
}
@ -236,7 +316,10 @@ mod timing_state_tests {
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_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
scenario.component.set_address_high_nibble(None);
@ -255,7 +338,10 @@ mod timing_state_tests {
let scenario = MockScenario::new("TestAccessTime");
// 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
// For now, we verify the getter works
@ -276,9 +362,14 @@ mod integration_scenarios {
assert_eq!(scenario.component.get_timing_state(), TimingState::Idle);
// 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));
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));
// Phase 2: Address phase - low nibble
@ -293,8 +384,13 @@ mod integration_scenarios {
assert_eq!(scenario.component.get_full_address_ready(), true);
// Phase 3: Wait for latency
scenario.component.set_timing_state(TimingState::WaitLatency);
assert_eq!(scenario.component.get_timing_state(), TimingState::WaitLatency);
scenario
.component
.set_timing_state(TimingState::WaitLatency);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::WaitLatency
);
// Advance time past access time
scenario.advance_time(scenario.component.get_access_time() + Duration::from_nanos(10));
@ -302,7 +398,10 @@ mod integration_scenarios {
// Phase 4: Drive data
scenario.component.set_timing_state(TimingState::DriveData);
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);
// Cycle complete - return to idle
@ -322,7 +421,10 @@ mod integration_scenarios {
for i in 0..4 {
let pin_name = format!("D{}", i);
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);
// Standard pins should exist
assert_eq!(scenario.component.get_pin_value("D0"), 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));
assert_eq!(
scenario.component.get_pin_value("D0"),
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]
@ -381,7 +492,10 @@ mod error_handling_tests {
// Test setting pin to HighZ (tri-state)
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
// (This would be more relevant in actual hardware interface tests)
@ -389,4 +503,4 @@ mod error_handling_tests {
// The exact behavior depends on how HighZ pins are interpreted
// This test documents the expected structure
}
}
}

View file

@ -4,11 +4,11 @@
//! to enable comprehensive testing of the intel_400x traits and functionality.
use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::pin::{Pin, PinValue};
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::{Pin, PinValue};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use std::collections::HashMap;
/// Mock pin implementation for testing
#[derive(Debug, Clone)]
@ -77,8 +77,15 @@ impl MockIntel400xComponent {
let data_pins = ["D0", "D1", "D2", "D3"];
let control_pins = ["SYNC", "CM", "RESET"];
for pin_name in clock_pins.iter().chain(data_pins.iter()).chain(control_pins.iter()) {
pins.insert(pin_name.to_string(), Arc::new(Mutex::new(MockPin::new(pin_name))));
for pin_name in clock_pins
.iter()
.chain(data_pins.iter())
.chain(control_pins.iter())
{
pins.insert(
pin_name.to_string(),
Arc::new(Mutex::new(MockPin::new(pin_name))),
);
}
Self {
@ -118,7 +125,11 @@ impl MockIntel400xComponent {
pub fn set_data_bus(&self, value: u8) {
for i in 0..4 {
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);
}
}
@ -312,27 +323,33 @@ impl MockScenario {
}
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) {
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) {
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) {
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) {
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) {
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) {
@ -355,7 +372,7 @@ impl MockScenario {
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
// For now, it's a placeholder
}
@ -418,4 +435,4 @@ pub mod proptest_helpers {
pub fn arb_duration() -> impl Strategy<Value = Duration> {
(0u64..1_000_000_000u64).prop_map(Duration::from_nanos)
}
}
}

View file

@ -3,10 +3,10 @@
//! These tests use property-based testing to verify the correctness
//! 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 proptest::prelude::*;
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use std::time::{Duration, Instant};
#[cfg(test)]
@ -100,7 +100,7 @@ mod state_machine_properties {
(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_low_nibble(Some(low));
scenario.component.set_full_address_ready(true);
@ -176,7 +176,7 @@ mod state_machine_properties {
// Test duration properties
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
prop_assert!(TimingConstants::DEFAULT_ACCESS_TIME > Duration::from_nanos(0));
@ -222,7 +222,10 @@ mod timing_invariant_tests {
if idle {
assert_eq!(active_count, 0, "Idle state should not have active flags");
} 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");
// Test that pin states are consistent
let test_values = vec![
PinValue::Low,
PinValue::High,
PinValue::HighZ,
];
let test_values = vec![PinValue::Low, PinValue::High, PinValue::HighZ];
for value in test_values {
scenario.component.set_pin_value("D0", value);
@ -278,7 +277,7 @@ mod timing_invariant_tests {
scenario.component.set_pin_value("PHI1", 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));
}
}
@ -389,35 +388,43 @@ mod concurrency_safety_tests {
let scenario = Arc::new(MockScenario::new("TestThreadSafety"));
// Test that our mock can handle concurrent access patterns
let handles: Vec<_> = (0..4).map(|i| {
let scenario_clone: std::sync::Arc<MockScenario> = Arc::clone(&scenario);
thread::spawn(move || {
// Each thread tests different aspects
match i {
0 => {
// Note: This would need proper Arc<Mutex<>> handling for thread safety
// For now, we'll skip the mutable operation in this test
// Note: Thread safety testing would require proper Arc<Mutex<>> handling
let handles: Vec<_> = (0..4)
.map(|i| {
let scenario_clone: Arc<MockScenario> = Arc::clone(&scenario);
thread::spawn(move || {
// Each thread tests different aspects
match i {
0 => {
// Note: This would need proper Arc<Mutex<>> handling for thread safety
// For now, we'll skip the mutable operation in this test
// Note: Thread safety testing would require proper Arc<Mutex<>> handling
}
1 => {
scenario_clone.component.set_pin_value("D0", PinValue::High);
assert_eq!(
scenario_clone.component.get_pin_value("D0"),
Some(PinValue::High)
);
}
2 => {
scenario_clone.component.set_address_high_nibble(Some(0x12));
assert_eq!(
scenario_clone.component.get_address_high_nibble(),
Some(0x12)
);
}
3 => {
scenario_clone.advance_time(Duration::from_nanos(100));
}
_ => unreachable!(),
}
1 => {
scenario_clone.component.set_pin_value("D0", PinValue::High);
assert_eq!(scenario_clone.component.get_pin_value("D0"), Some(PinValue::High));
}
2 => {
scenario_clone.component.set_address_high_nibble(Some(0x12));
assert_eq!(scenario_clone.component.get_address_high_nibble(), Some(0x12));
}
3 => {
scenario_clone.advance_time(Duration::from_nanos(100));
}
_ => unreachable!(),
}
})
})
}).collect();
.collect();
// Wait for all threads to complete
for handle in handles {
handle.join().expect("Thread should complete successfully");
}
}
}
}

View file

@ -188,4 +188,4 @@ mod simple_tests {
println!("=== Timing configuration tests completed ===");
}
}
}

View file

@ -200,4 +200,4 @@ mod working_tests {
println!("✓ Error handling works correctly");
}
}
}