Created factory for system creation.
Systems are now defined in JSON rather than hard-coded
This commit is contained in:
parent
c039e9069a
commit
6f3295dc5e
31 changed files with 1610 additions and 1709 deletions
11
Cargo.toml
11
Cargo.toml
|
|
@ -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"
|
||||
27
README.md
27
README.md
|
|
@ -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
110
configs/mcs4_basic.json
Normal 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
167
configs/mcs4_max.json
Normal 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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
159
src/main.rs
159
src/main.rs
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
351
src/system_config.rs
Normal file
351
src/system_config.rs
Normal 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,
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pub mod intel_mcs_4;
|
||||
pub mod intel_mcs_4_max;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
22
tests/lib.rs
22
tests/lib.rs
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,31 +388,39 @@ 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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue