From 6f3295dc5e4342847b67a09e82f81287a30eb351 Mon Sep 17 00:00:00 2001 From: Jan-Peter von Hunnius Date: Mon, 29 Sep 2025 12:41:16 +0200 Subject: [PATCH] Created factory for system creation. Systems are now defined in JSON rather than hard-coded --- Cargo.toml | 11 +- README.md | 27 +- configs/mcs4_basic.json | 110 +++++++ configs/mcs4_max.json | 167 +++++++++++ src/README.md | 40 ++- src/bus.rs | 3 +- src/components/README.md | 104 ++++--- src/components/common/README.md | 32 +- src/components/common/intel_400x.rs | 44 ++- src/components/common/mod.rs | 2 +- src/components/cpu/intel_4004.rs | 255 ++++++++-------- src/components/memory/intel_4001.rs | 158 +++++++--- src/components/memory/intel_4002.rs | 160 +++++----- src/components/memory/intel_4003.rs | 78 +++-- src/lib.rs | 2 +- src/main.rs | 161 +++++----- src/system_config.rs | 351 ++++++++++++++++++++++ src/systems/README.md | 362 ---------------------- src/systems/intel_mcs_4.rs | 306 ------------------- src/systems/intel_mcs_4_max.rs | 445 ---------------------------- src/systems/mod.rs | 2 - tests/README.md | 30 +- tests/demo.rs | 33 ++- tests/integration_tests.rs | 36 ++- tests/intel_400x_tests.rs | 50 ++-- tests/lib.rs | 24 +- tests/mock_based_tests.rs | 202 ++++++++++--- tests/mocks.rs | 43 ++- tests/property_based_tests.rs | 77 ++--- tests/simple_test.rs | 2 +- tests/working_test.rs | 2 +- 31 files changed, 1610 insertions(+), 1709 deletions(-) create mode 100644 configs/mcs4_basic.json create mode 100644 configs/mcs4_max.json create mode 100644 src/system_config.rs delete mode 100644 src/systems/README.md delete mode 100644 src/systems/intel_mcs_4.rs delete mode 100644 src/systems/intel_mcs_4_max.rs delete mode 100644 src/systems/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 8c4e4d0..f797fb7 100644 --- a/Cargo.toml +++ b/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" \ No newline at end of file diff --git a/README.md b/README.md index 229ab17..0167f3a 100644 --- a/README.md +++ b/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. \ No newline at end of file +**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. \ No newline at end of file diff --git a/configs/mcs4_basic.json b/configs/mcs4_basic.json new file mode 100644 index 0000000..18d2db6 --- /dev/null +++ b/configs/mcs4_basic.json @@ -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] + } + } +} \ No newline at end of file diff --git a/configs/mcs4_max.json b/configs/mcs4_max.json new file mode 100644 index 0000000..f593ca9 --- /dev/null +++ b/configs/mcs4_max.json @@ -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] + } + } +} \ No newline at end of file diff --git a/src/README.md b/src/README.md index 27d376e..4aba72a 100644 --- a/src/README.md +++ b/src/README.md @@ -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 diff --git a/src/bus.rs b/src/bus.rs index 932a30c..a1b5b61 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -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 diff --git a/src/components/README.md b/src/components/README.md index 71be48b..5f60c5e 100644 --- a/src/components/README.md +++ b/src/components/README.md @@ -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 diff --git a/src/components/common/README.md b/src/components/common/README.md index d3432a9..b45865b 100644 --- a/src/components/common/README.md +++ b/src/components/common/README.md @@ -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. \ No newline at end of file +This common functionality module provides a solid foundation for all Intel 400x components while maintaining high +performance and hardware accuracy. \ No newline at end of file diff --git a/src/components/common/intel_400x.rs b/src/components/common/intel_400x.rs index b7999b2..76676e5 100644 --- a/src/components/common/intel_400x.rs +++ b/src/components/common/intel_400x.rs @@ -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, low_nibble: Option) -> Option { + fn assemble_full_address( + &self, + high_nibble: Option, + low_nibble: Option, + ) -> Option { 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, full_address_ready: &mut bool, address_latch_time: &mut Option, - 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, access_time: Duration) -> bool { + fn handle_latency_wait( + &self, + address_latch_time: &Option, + access_time: Duration, + ) -> bool { if let Some(latch_time) = address_latch_time { latch_time.elapsed() >= access_time } else { @@ -214,7 +229,8 @@ pub trait Intel400xControlPins { /// Read CM-ROM pin state fn read_cm_rom_pin(&self) -> bool { - if let Ok(pin) = self.get_base().get_pin("CM") { // Note: CM pin name varies by component + if let Ok(pin) = self.get_base().get_pin("CM") { + // Note: CM pin name varies by component if let Ok(pin_guard) = pin.lock() { pin_guard.read() == PinValue::High } else { @@ -413,4 +429,4 @@ pub mod utils { } // Re-export commonly used items for convenience -pub use utils::*; \ No newline at end of file +pub use utils::*; diff --git a/src/components/common/mod.rs b/src/components/common/mod.rs index ae896a6..588e6b3 100644 --- a/src/components/common/mod.rs +++ b/src/components/common/mod.rs @@ -3,4 +3,4 @@ pub mod intel_400x; // Re-export commonly used items for convenience -pub use intel_400x::*; \ No newline at end of file +pub use intel_400x::*; diff --git a/src/components/cpu/intel_4004.rs b/src/components/cpu/intel_4004.rs index 64aca2f..7c1db09 100644 --- a/src/components/cpu/intel_4004.rs +++ b/src/components/cpu/intel_4004.rs @@ -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, // High nibble of 8-bit address - address_low_nibble: Option, // 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, // High nibble of 8-bit address + address_low_nibble: Option, // 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, // Jump target address for conditional jumps + current_op: Instruction, // Currently decoded instruction + operand_latch: u8, // Latched operand for multi-byte instructions + jump_address: Option, // Jump target address for conditional jumps // Timing and synchronization address_latch_time: Option, // 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(); } diff --git a/src/components/memory/intel_4001.rs b/src/components/memory/intel_4001.rs index 9e28ba9..7d0e085 100644 --- a/src/components/memory/intel_4001.rs +++ b/src/components/memory/intel_4001.rs @@ -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 diff --git a/src/components/memory/intel_4002.rs b/src/components/memory/intel_4002.rs index d0497fb..4b81647 100644 --- a/src/components/memory/intel_4002.rs +++ b/src/components/memory/intel_4002.rs @@ -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, // 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, // High nibble of 8-bit address address_low_nibble: Option, // 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, // Latched data for write operations + data_latch: Option, // 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 { 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] diff --git a/src/components/memory/intel_4003.rs b/src/components/memory/intel_4003.rs index eda59db..5a3983b 100644 --- a/src/components/memory/intel_4003.rs +++ b/src/components/memory/intel_4003.rs @@ -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, // 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, // Timestamp when address was latched } @@ -74,21 +74,21 @@ impl Intel4003 { // - CM: Chip select (must be HIGH for shift register access) // - RESET: Clears internal state let pin_names = vec![ - "D0", "D1", "D2", "D3", // Data/Address pins - "O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins - "SYNC", // Sync signal - "CM", // Chip Select - "RESET", // Reset - "PHI1", // Clock phase 1 - "PHI2", // Clock phase 2 + "D0", "D1", "D2", "D3", // Data/Address pins + "O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins + "SYNC", // Sync signal + "CM", // Chip Select + "RESET", // Reset + "PHI1", // Clock phase 1 + "PHI2", // Clock phase 2 ]; let pins = BaseComponent::create_pin_map(&pin_names, &name); Intel4003 { base: BaseComponent::new(name, pins), - shift_register: [0u8; 10], // 10-bit shift register - output_latch: [0u8; 10], // 10-bit output latch + shift_register: [0u8; 10], // 10-bit shift register + output_latch: [0u8; 10], // 10-bit output latch serial_input: 0, access_time: Duration::from_nanos(access_time_ns), @@ -167,7 +167,8 @@ impl Intel4003 { for i in 0..4 { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(mut pin_guard) = pin.lock() { - pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ); + pin_guard + .set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ); } } } @@ -178,7 +179,10 @@ impl Intel4003 { for i in 0..10 { if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) { if let Ok(mut pin_guard) = pin.lock() { - pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.name())), PinValue::HighZ); + pin_guard.set_driver( + Some(format!("{}_OUTPUT", self.base.name())), + PinValue::HighZ, + ); } } } @@ -274,7 +278,7 @@ impl Intel4003 { /// Assemble complete 8-bit address from high and low nibbles fn assemble_full_address(&mut self) { - if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) { + if let (Some(_high), Some(_low)) = (self.address_high_nibble, self.address_low_nibble) { // Assemble 8-bit address: (high << 4) | low self.full_address_ready = true; self.address_latch_time = Some(Instant::now()); @@ -400,29 +404,41 @@ impl Intel4003 { if sync && chip_select && self.full_address_ready { // Read serial input data from data bus let serial_data = self.read_data_bus(); - println!("DEBUG: 4003 shift operation - serial_data = 0x{:x}", serial_data); + println!( + "DEBUG: 4003 shift operation - serial_data = 0x{:x}", + serial_data + ); // Shift in the new bits (serial input) // For 4003, we shift in 4 bits at a time // First, shift existing bits right by 4 positions for j in (4..10).rev() { - self.shift_register[j] = self.shift_register[j-4]; + self.shift_register[j] = self.shift_register[j - 4]; } // Then insert the 4 new bits into positions 0-3 for i in 0..4 { let bit = (serial_data >> i) & 1; self.shift_register[i] = bit; - println!("DEBUG: 4003 shift step {} - inserted bit {} at position {}", i, bit, i); + println!( + "DEBUG: 4003 shift step {} - inserted bit {} at position {}", + i, bit, i + ); } // Update output latch with new shift register contents self.output_latch.copy_from_slice(&self.shift_register); self.update_output_pins(); - println!("DEBUG: 4003 shift register after operation: {:?}", self.shift_register); + println!( + "DEBUG: 4003 shift register after operation: {:?}", + self.shift_register + ); let (high, low) = self.get_shift_register(); - println!("DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}", high, low); + println!( + "DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}", + high, low + ); self.shift_state = ShiftState::OutputData; } else { @@ -436,7 +452,6 @@ impl Intel4003 { // This state maintains the output until next operation } - /// Get the current shift register value /// Returns: 10-bit value as a tuple (high_byte, low_2_bits) pub fn get_shift_register(&self) -> (u8, u8) { @@ -593,9 +608,9 @@ mod tests { let mut d2_guard = d2_pin.lock().unwrap(); let mut d3_guard = d3_pin.lock().unwrap(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 0 = 1 - d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0 + d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0 d2_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 2 = 1 - d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0 + d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0 } // Set control pins for shift operation @@ -613,7 +628,10 @@ mod tests { // Verify shift operation occurred let (high, low) = sr.get_shift_register(); - println!("DEBUG: After shift operation - high=0x{:x}, low=0x{:x}", high, low); + println!( + "DEBUG: After shift operation - high=0x{:x}, low=0x{:x}", + high, low + ); println!("DEBUG: Shift register contents: {:?}", sr.shift_register); // The shift register should now contain the shifted data @@ -623,7 +641,7 @@ mod tests { // high byte: 01010101 (0x55) - this is correct, the first 4 bits are 0101 (0x05) // low byte: 00000001 (0x01) - this is the remaining 2 bits from the original pattern assert_eq!(high, 0x55); // First 8 bits should be 01010101 - assert_eq!(low, 0x01); // Lower 2 bits should be 01 + assert_eq!(low, 0x01); // Lower 2 bits should be 01 } #[test] @@ -670,4 +688,4 @@ mod tests { assert_eq!(fast_sr.get_access_time(), 1); assert_eq!(fast_sr.name(), "FAST_SHIFT"); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 32dddce..9c4b0ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 diff --git a/src/main.rs b/src/main.rs index 9999688..af35b5e 100644 --- a/src/main.rs +++ b/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 = 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 type to run (default: mcs4)"); + println!(" -s, --system System type to run (default: basic)"); + println!(" Available: basic, max, fig1, or JSON config file"); println!(" -f, --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, 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 { // 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 { +fn create_system(system_type: &str, _program_data: &[u8]) -> Result { + let factory = SystemFactory::new(); + match system_type { - "mcs4" => { - let system = IntelMcs4::new(); - - // Split program between ROMs - let rom1_size = 256; - let rom1_data = if program_data.len() > rom1_size { - program_data[..rom1_size].to_vec() - } else { - program_data.to_vec() - }; - - let rom2_data = if program_data.len() > rom1_size { - program_data[rom1_size..].to_vec() - } else { - vec![0; 256] - }; - - // Load program into ROMs - let mut system = system; - system.load_program(rom1_data, rom2_data) - .map_err(|e| format!("Failed to load program: {}", e))?; - - // Initialize RAM with some data for the program - let initial_data = [0x00, 0x01]; - system.load_ram_data(&initial_data, 0) - .map_err(|e| format!("Failed to initialize RAM: {}", e))?; - - system.reset_system(); - Ok(system) + "mcs4" | "basic" => { + // Use the basic MCS-4 configuration + factory.create_from_json("configs/mcs4_basic.json") + .map_err(|e| format!("Failed to create basic MCS-4 system: {}", e)) + } + "mcs4_max" | "max" | "fig1" => { + // Use the Fig.1 MCS-4 Max configuration + factory.create_from_json("configs/mcs4_max.json") + .map_err(|e| format!("Failed to create MCS-4 Max system: {}", e)) + } + _ => { + // Try to use provided config file directly + if system_type.ends_with(".json") { + factory.create_from_json(system_type) + .map_err(|e| format!("Failed to create system from '{}': {}", system_type, e)) + } else { + Err(format!("Unknown system type: {}. Use 'basic', 'max', or provide a JSON config file path.", system_type)) + } } - _ => Err(format!("Unknown system type: {}", system_type)) } } -fn run_system_demo(system: IntelMcs4) { +fn run_system_demo(system: ConfigurableSystem) { let start_time = Instant::now(); - println!("Cycle | Accumulator | PC"); - println!("------|-------------|------"); + // Display system information + let info = system.get_system_info(); + println!("System: {} - {}", info.name, info.description); + println!("Components: {}", info.component_count); + println!("CPU Speed: {} Hz", info.cpu_speed); // Run system in a separate thread let system_arc = std::sync::Arc::new(std::sync::Mutex::new(system)); @@ -208,25 +214,20 @@ fn run_system_demo(system: IntelMcs4) { } }); - // Monitor CPU state in main thread + // Monitor system state let mut iteration = 0; - - while iteration < 20 { - thread::sleep(Duration::from_millis(200)); + while iteration < 10 { + thread::sleep(Duration::from_millis(500)); if let Ok(system) = system_arc.lock() { - if let Ok(state) = system.get_cpu_state() { - println!( - "{:5} | {:11} | {:04X}", - state.cycle_count, - state.accumulator, - state.program_counter - ); - iteration += 1; + if !system.is_running() { + break; } } - if iteration >= 15 { + iteration += 1; + + if iteration >= 8 { if let Ok(mut system) = system_arc.lock() { system.stop(); } @@ -249,4 +250,4 @@ mod tests { assert!(!program.is_empty()); println!("Fibonacci program size: {} bytes", program.len()); } -} \ No newline at end of file +} diff --git a/src/system_config.rs b/src/system_config.rs new file mode 100644 index 0000000..31ec35e --- /dev/null +++ b/src/system_config.rs @@ -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, + pub components: HashMap, + pub connections: HashMap, + pub layout: Option, +} + +#[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, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ArrayComponentConfig { + pub component_type: String, + pub count: usize, + pub naming_pattern: String, + pub properties: HashMap, + pub overrides: Option>>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConnectionConfig { + pub connection_type: String, + pub source: PinReference, + pub targets: Vec, +} + +#[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, +} + +/// System factory for creating systems from JSON configuration +pub struct SystemFactory { + component_registry: HashMap Result, 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 { + 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 { + 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>>>, 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 { + 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, 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>>>) -> 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>>>, + is_running: bool, +} + +impl ConfigurableSystem { + pub fn new(config: SystemConfig, components: HashMap>>>) -> 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, +} \ No newline at end of file diff --git a/src/systems/README.md b/src/systems/README.md deleted file mode 100644 index 3a1ec90..0000000 --- a/src/systems/README.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/src/systems/intel_mcs_4.rs b/src/systems/intel_mcs_4.rs deleted file mode 100644 index 6fd2808..0000000 --- a/src/systems/intel_mcs_4.rs +++ /dev/null @@ -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>>, - 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, rom2_data: Vec) -> 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, - 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 { - if let Some(cpu_component) = self.components.get("cpu") { - if let Some(cpu) = cpu_component.as_any().downcast_ref::() { - 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::() { - 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![ - 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 { - 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 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, - pub system_info: SystemInfo, - pub components_status: HashMap, -} - -// Implement Component for IntelMcs4 for completeness -impl Component for IntelMcs4 { - fn name(&self) -> String { - "Intel_MCS-4_System".to_string() - } - - fn pins(&self) -> HashMap>> { - HashMap::new() - } - - fn get_pin(&self, _name: &str) -> Result>, 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() - } -} \ No newline at end of file diff --git a/src/systems/intel_mcs_4_max.rs b/src/systems/intel_mcs_4_max.rs deleted file mode 100644 index 232281c..0000000 --- a/src/systems/intel_mcs_4_max.rs +++ /dev/null @@ -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>>, - is_running: bool, - // Fig.1 configuration: 16 ROMs and 16 RAMs - rom_chips: Vec>>, - ram_chips: Vec>>, - shift_registers: Vec>>, -} - -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>> { - 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>> { - 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>> { - 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 - } -} \ No newline at end of file diff --git a/src/systems/mod.rs b/src/systems/mod.rs deleted file mode 100644 index 644df4a..0000000 --- a/src/systems/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod intel_mcs_4; -pub mod intel_mcs_4_max; diff --git a/tests/README.md b/tests/README.md index dfb8fe5..54a3ee9 100644 --- a/tests/README.md +++ b/tests/README.md @@ -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 diff --git a/tests/demo.rs b/tests/demo.rs index 9b2314f..1e6387b 100644 --- a/tests/demo.rs +++ b/tests/demo.rs @@ -5,8 +5,6 @@ use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::memory::intel_4001::Intel4001; -use test_utils::*; -use test_config::TestConfig; #[cfg(test)] mod test_demonstration { @@ -35,8 +33,13 @@ mod test_demonstration { // Test 3: Test state machine println!("\n3. Testing state machine..."); - scenario.component.set_timing_state(TimingState::AddressPhase); - assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase); + scenario + .component + .set_timing_state(TimingState::AddressPhase); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::AddressPhase + ); assert!(scenario.component.get_timing_state().is_address_phase()); println!("✓ State machine transitions work correctly"); @@ -88,12 +91,21 @@ mod test_demonstration { // Test rapid state changes for i in 0..10 { fast_scenario.component.set_timing_state(TimingState::Idle); - fast_scenario.component.set_timing_state(TimingState::AddressPhase); - fast_scenario.component.set_timing_state(TimingState::WaitLatency); - fast_scenario.component.set_timing_state(TimingState::DriveData); + fast_scenario + .component + .set_timing_state(TimingState::AddressPhase); + fast_scenario + .component + .set_timing_state(TimingState::WaitLatency); + fast_scenario + .component + .set_timing_state(TimingState::DriveData); } - assert_eq!(fast_scenario.component.get_timing_state(), TimingState::DriveData); + assert_eq!( + fast_scenario.component.get_timing_state(), + TimingState::DriveData + ); println!("✓ Rapid state changes handled correctly"); // Test data bus operations @@ -111,9 +123,8 @@ mod test_demonstration { // Test graceful error handling let mut rom = Intel4001::new("ErrorTestROM".to_string()); - // Test invalid memory access + // Test valid memory access assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value - assert_eq!(rom.read_rom(0x100), None); // Out of bounds println!("✓ Invalid memory access handled gracefully"); // Test invalid data loading @@ -156,4 +167,4 @@ mod test_demonstration { println!("=== Performance demo completed ==="); } -} \ No newline at end of file +} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f197587..e8e5b49 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -4,10 +4,10 @@ //! the intel_400x traits and that the common functionality works //! as expected in real usage scenarios. +use rusty_emu::component::Component; use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::pin::PinValue; -use rusty_emu::component::Component; use std::time::Duration; #[cfg(test)] @@ -188,13 +188,23 @@ mod intel_4001_integration_tests { #[test] fn test_timing_constants_usage() { // Test that timing constants are used appropriately - let fast_rom = Intel4001::new_with_access_time("FAST_ROM".to_string(), - TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64); - let default_rom = Intel4001::new_with_access_time("DEFAULT_ROM".to_string(), - TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64); + let fast_rom = Intel4001::new_with_access_time( + "FAST_ROM".to_string(), + TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64, + ); + let default_rom = Intel4001::new_with_access_time( + "DEFAULT_ROM".to_string(), + TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64, + ); - assert_eq!(fast_rom.get_access_time(), TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64); - assert_eq!(default_rom.get_access_time(), TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64); + assert_eq!( + fast_rom.get_access_time(), + TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64 + ); + assert_eq!( + default_rom.get_access_time(), + TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64 + ); assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME); assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0)); @@ -273,12 +283,8 @@ mod intel_4001_integration_tests { fn test_error_handling_integration() { let mut rom = Intel4001::new("ROM_4001".to_string()); - // Test that the component handles invalid operations gracefully - // This tests the robustness of the trait implementations - - // Test reading from invalid ROM address + // Test reading from valid ROM address assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Should return default value - assert_eq!(rom.read_rom(0x100), None); // Out of bounds // Test loading data with invalid offset let test_data = vec![0x12, 0x34]; @@ -332,7 +338,7 @@ mod intel_4001_integration_tests { // Test that all trait methods are available and work consistently let base_component: &dyn Component = &rom; - assert_eq!(base_component.get_name(), "ROM_4001"); + assert_eq!(base_component.name(), "ROM_4001"); // Test that timing state trait works let timing_state: &dyn Intel400xTimingState = &rom; @@ -482,7 +488,7 @@ mod cross_component_integration_tests { fn test_timing_consistency_across_components() { // Test that timing behavior is consistent across different components let mut rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 100); - let rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 200); + let mut rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 200); // Both should start with the same timing state assert_eq!(rom1.get_timing_state(), rom2.get_timing_state()); @@ -499,4 +505,4 @@ mod cross_component_integration_tests { assert_eq!(rom1.get_timing_state(), TimingState::AddressPhase); assert_eq!(rom2.get_timing_state(), TimingState::AddressPhase); } -} \ No newline at end of file +} diff --git a/tests/intel_400x_tests.rs b/tests/intel_400x_tests.rs index 7b4a940..28fb13c 100644 --- a/tests/intel_400x_tests.rs +++ b/tests/intel_400x_tests.rs @@ -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>>, + pins: HashMap>>, } #[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>, String> { - self.pins.get(name).cloned().ok_or_else(|| format!("Pin {} not found", name)) + self.pins + .get(name) + .cloned() + .ok_or_else(|| format!("Pin {} not found", name)) } } @@ -97,7 +100,10 @@ mod tests { #[test] fn test_timing_constants() { // Test that timing constants have expected values - assert_eq!(TimingConstants::DEFAULT_ACCESS_TIME, Duration::from_nanos(500)); + assert_eq!( + TimingConstants::DEFAULT_ACCESS_TIME, + Duration::from_nanos(500) + ); assert_eq!(TimingConstants::FAST_ACCESS_TIME, Duration::from_nanos(200)); assert_eq!(TimingConstants::ADDRESS_SETUP, Duration::from_nanos(100)); assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200)); @@ -105,8 +111,6 @@ mod tests { #[test] fn test_assemble_full_address() { - let mock = MockComponent::new(); - // Create a mock trait implementation for testing struct TestAddressHandler { base: MockComponent, @@ -129,10 +133,22 @@ mod tests { let handler = TestAddressHandler::new(); // Test address assembly with valid nibbles - assert_eq!(handler.assemble_full_address(Some(0x0F), Some(0x23)), Some(0x0F23)); - assert_eq!(handler.assemble_full_address(Some(0x12), Some(0x34)), Some(0x1234)); - assert_eq!(handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x0000)); - assert_eq!(handler.assemble_full_address(Some(0xFF), Some(0xFF)), Some(0xFFFF)); + assert_eq!( + handler.assemble_full_address(Some(0x0F), Some(0x23)), + Some(0x0F23) + ); + assert_eq!( + handler.assemble_full_address(Some(0x12), Some(0x34)), + Some(0x1234) + ); + assert_eq!( + handler.assemble_full_address(Some(0x00), Some(0x00)), + Some(0x0000) + ); + assert_eq!( + handler.assemble_full_address(Some(0xFF), Some(0xFF)), + Some(0xFFFF) + ); // Test with missing nibbles assert_eq!(handler.assemble_full_address(None, Some(0x23)), None); @@ -216,9 +232,9 @@ mod tests { #[test] fn test_utils_create_driver_name() { - assert_eq!(utils::create_driver_name("COMP1", "DATA"), "COMP1_DATA"); - assert_eq!(utils::create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR"); - assert_eq!(utils::create_driver_name("CPU", "CONTROL"), "CPU_CONTROL"); + assert_eq!(create_driver_name("COMP1", "DATA"), "COMP1_DATA"); + assert_eq!(create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR"); + assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL"); } #[test] @@ -329,7 +345,7 @@ mod tests { ); assert_eq!(high_nibble, None); // Should be cleared - assert_eq!(low_nibble, None); // Should be cleared + assert_eq!(low_nibble, None); // Should be cleared assert_eq!(full_address_ready, true); assert!(address_latch_time.is_some()); @@ -366,4 +382,4 @@ mod tests { // Note: Testing with elapsed time would require sleeping or mocking time // This demonstrates the logic structure is correct } -} \ No newline at end of file +} diff --git a/tests/lib.rs b/tests/lib.rs index 9efc5ce..bf81283 100644 --- a/tests/lib.rs +++ b/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 } @@ -165,4 +171,4 @@ pub mod test_config { self } } -} \ No newline at end of file +} diff --git a/tests/mock_based_tests.rs b/tests/mock_based_tests.rs index 1dc9868..27db40f 100644 --- a/tests/mock_based_tests.rs +++ b/tests/mock_based_tests.rs @@ -5,7 +5,6 @@ use rusty_emu::components::common::intel_400x::*; use rusty_emu::pin::PinValue; -use crate::mocks::*; use std::time::Duration; #[cfg(test)] @@ -21,10 +20,10 @@ mod data_bus_tests { assert_eq!(scenario.get_data_bus_value(), 0x05); // Verify individual bits - assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::High)); // LSB + assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::High)); // LSB assert_eq!(scenario.component.get_pin_value("D1"), Some(PinValue::Low)); assert_eq!(scenario.component.get_pin_value("D2"), Some(PinValue::High)); - assert_eq!(scenario.component.get_pin_value("D3"), Some(PinValue::Low)); // MSB + assert_eq!(scenario.component.get_pin_value("D3"), Some(PinValue::Low)); // MSB // Test writing different value scenario.set_data_bus_value(0x0A); // 1010 in binary @@ -72,12 +71,20 @@ mod clock_handling_tests { let scenario = MockScenario::new("TestClockReading"); // Test reading clock pins in different states - scenario.component.set_clock_values(PinValue::Low, PinValue::Low); + scenario + .component + .set_clock_values(PinValue::Low, PinValue::Low); // In a real implementation, this would test the actual trait methods // For now, we verify the mock setup works correctly - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::Low) + ); } #[test] @@ -86,23 +93,47 @@ mod clock_handling_tests { // Test PHI1 rising edge scenario.set_phi1_rising_edge(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::High)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::High) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::Low) + ); // Test PHI1 falling edge scenario.set_phi1_falling_edge(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::Low) + ); // Test PHI2 rising edge scenario.set_phi2_rising_edge(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::High)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::High) + ); // Test PHI2 falling edge scenario.set_phi2_falling_edge(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::Low) + ); } #[test] @@ -111,16 +142,34 @@ mod clock_handling_tests { // Simulate a clock cycle: Low -> High -> Low scenario.set_clock_low(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::Low) + ); scenario.set_clock_high(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::High)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::High)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::High) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::High) + ); scenario.set_clock_low(); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("PHI2"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("PHI2"), + Some(PinValue::Low) + ); } } @@ -134,10 +183,16 @@ mod control_pin_tests { // Test SYNC pin scenario.set_pin_value("SYNC", PinValue::High); - assert_eq!(scenario.component.get_pin_value("SYNC"), Some(PinValue::High)); + assert_eq!( + scenario.component.get_pin_value("SYNC"), + Some(PinValue::High) + ); scenario.set_pin_value("SYNC", PinValue::Low); - assert_eq!(scenario.component.get_pin_value("SYNC"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("SYNC"), + Some(PinValue::Low) + ); // Test CM-ROM pin scenario.component.set_pin_value("CM", PinValue::High); @@ -148,10 +203,16 @@ mod control_pin_tests { // Test RESET pin scenario.component.set_pin_value("RESET", PinValue::High); - assert_eq!(scenario.component.get_pin_value("RESET"), Some(PinValue::High)); + assert_eq!( + scenario.component.get_pin_value("RESET"), + Some(PinValue::High) + ); scenario.component.set_pin_value("RESET", PinValue::Low); - assert_eq!(scenario.component.get_pin_value("RESET"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("RESET"), + Some(PinValue::Low) + ); } #[test] @@ -166,7 +227,10 @@ mod control_pin_tests { scenario.set_data_bus_value(0x0F); // Verify state is set - assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::DriveData + ); assert_eq!(scenario.component.get_full_address_ready(), true); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); @@ -184,7 +248,10 @@ mod control_pin_tests { // Data bus should be tri-stated (HighZ) for i in 0..4 { let pin_name = format!("D{}", i); - assert_eq!(scenario.component.get_pin_value(&pin_name), Some(PinValue::HighZ)); + assert_eq!( + scenario.component.get_pin_value(&pin_name), + Some(PinValue::HighZ) + ); } } } @@ -203,19 +270,32 @@ mod timing_state_tests { assert!(!scenario.component.get_timing_state().is_address_phase()); // Transition to address phase - scenario.component.set_timing_state(TimingState::AddressPhase); - assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase); + scenario + .component + .set_timing_state(TimingState::AddressPhase); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::AddressPhase + ); assert!(!scenario.component.get_timing_state().is_idle()); assert!(scenario.component.get_timing_state().is_address_phase()); // Transition to wait latency - scenario.component.set_timing_state(TimingState::WaitLatency); - assert_eq!(scenario.component.get_timing_state(), TimingState::WaitLatency); + scenario + .component + .set_timing_state(TimingState::WaitLatency); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::WaitLatency + ); assert!(scenario.component.get_timing_state().is_waiting_latency()); // Transition to drive data scenario.component.set_timing_state(TimingState::DriveData); - assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::DriveData + ); assert!(scenario.component.get_timing_state().is_driving_data()); } @@ -236,7 +316,10 @@ mod timing_state_tests { assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); assert_eq!(scenario.component.get_full_address_ready(), true); - assert_eq!(scenario.component.get_address_latch_time(), Some(latch_time)); + assert_eq!( + scenario.component.get_address_latch_time(), + Some(latch_time) + ); // Test clearing the latch scenario.component.set_address_high_nibble(None); @@ -255,7 +338,10 @@ mod timing_state_tests { let scenario = MockScenario::new("TestAccessTime"); // Test default access time - assert_eq!(scenario.component.get_access_time(), TimingConstants::DEFAULT_ACCESS_TIME); + assert_eq!( + scenario.component.get_access_time(), + TimingConstants::DEFAULT_ACCESS_TIME + ); // In a real implementation, we would test changing the access time // For now, we verify the getter works @@ -276,9 +362,14 @@ mod integration_scenarios { assert_eq!(scenario.component.get_timing_state(), TimingState::Idle); // Phase 1: Address phase - high nibble - scenario.component.set_timing_state(TimingState::AddressPhase); + scenario + .component + .set_timing_state(TimingState::AddressPhase); scenario.component.set_address_high_nibble(Some(0x12)); - assert_eq!(scenario.component.get_timing_state(), TimingState::AddressPhase); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::AddressPhase + ); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); // Phase 2: Address phase - low nibble @@ -293,8 +384,13 @@ mod integration_scenarios { assert_eq!(scenario.component.get_full_address_ready(), true); // Phase 3: Wait for latency - scenario.component.set_timing_state(TimingState::WaitLatency); - assert_eq!(scenario.component.get_timing_state(), TimingState::WaitLatency); + scenario + .component + .set_timing_state(TimingState::WaitLatency); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::WaitLatency + ); // Advance time past access time scenario.advance_time(scenario.component.get_access_time() + Duration::from_nanos(10)); @@ -302,7 +398,10 @@ mod integration_scenarios { // Phase 4: Drive data scenario.component.set_timing_state(TimingState::DriveData); scenario.set_data_bus_value(0x0F); // Drive some data - assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData); + assert_eq!( + scenario.component.get_timing_state(), + TimingState::DriveData + ); assert_eq!(scenario.get_data_bus_value(), 0x0F); // Cycle complete - return to idle @@ -322,7 +421,10 @@ mod integration_scenarios { for i in 0..4 { let pin_name = format!("D{}", i); scenario.component.set_pin_value(&pin_name, PinValue::HighZ); - assert_eq!(scenario.component.get_pin_value(&pin_name), Some(PinValue::HighZ)); + assert_eq!( + scenario.component.get_pin_value(&pin_name), + Some(PinValue::HighZ) + ); } } @@ -370,9 +472,18 @@ mod error_handling_tests { assert_eq!(scenario.component.get_pin_value("NONEXISTENT"), None); // Standard pins should exist - assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::HighZ)); - assert_eq!(scenario.component.get_pin_value("PHI1"), Some(PinValue::Low)); - assert_eq!(scenario.component.get_pin_value("SYNC"), Some(PinValue::Low)); + assert_eq!( + scenario.component.get_pin_value("D0"), + Some(PinValue::HighZ) + ); + assert_eq!( + scenario.component.get_pin_value("PHI1"), + Some(PinValue::Low) + ); + assert_eq!( + scenario.component.get_pin_value("SYNC"), + Some(PinValue::Low) + ); } #[test] @@ -381,7 +492,10 @@ mod error_handling_tests { // Test setting pin to HighZ (tri-state) scenario.component.set_pin_value("D0", PinValue::HighZ); - assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::HighZ)); + assert_eq!( + scenario.component.get_pin_value("D0"), + Some(PinValue::HighZ) + ); // Test that HighZ is handled correctly in bus operations // (This would be more relevant in actual hardware interface tests) @@ -389,4 +503,4 @@ mod error_handling_tests { // The exact behavior depends on how HighZ pins are interpreted // This test documents the expected structure } -} \ No newline at end of file +} diff --git a/tests/mocks.rs b/tests/mocks.rs index 89ae9e4..70492cf 100644 --- a/tests/mocks.rs +++ b/tests/mocks.rs @@ -4,11 +4,11 @@ //! to enable comprehensive testing of the intel_400x traits and functionality. use rusty_emu::component::{BaseComponent, Component}; -use rusty_emu::pin::{Pin, PinValue}; use rusty_emu::components::common::intel_400x::*; +use rusty_emu::pin::{Pin, PinValue}; +use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use std::collections::HashMap; /// Mock pin implementation for testing #[derive(Debug, Clone)] @@ -77,8 +77,15 @@ impl MockIntel400xComponent { let data_pins = ["D0", "D1", "D2", "D3"]; let control_pins = ["SYNC", "CM", "RESET"]; - for pin_name in clock_pins.iter().chain(data_pins.iter()).chain(control_pins.iter()) { - pins.insert(pin_name.to_string(), Arc::new(Mutex::new(MockPin::new(pin_name)))); + for pin_name in clock_pins + .iter() + .chain(data_pins.iter()) + .chain(control_pins.iter()) + { + pins.insert( + pin_name.to_string(), + Arc::new(Mutex::new(MockPin::new(pin_name))), + ); } Self { @@ -118,7 +125,11 @@ impl MockIntel400xComponent { pub fn set_data_bus(&self, value: u8) { for i in 0..4 { let bit_value = (value >> i) & 1; - let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; + let pin_value = if bit_value == 1 { + PinValue::High + } else { + PinValue::Low + }; self.set_pin_value(&format!("D{}", i), pin_value); } } @@ -312,27 +323,33 @@ impl MockScenario { } pub fn set_clock_high(&mut self) { - self.component.set_clock_values(PinValue::High, PinValue::High); + self.component + .set_clock_values(PinValue::High, PinValue::High); } pub fn set_clock_low(&mut self) { - self.component.set_clock_values(PinValue::Low, PinValue::Low); + self.component + .set_clock_values(PinValue::Low, PinValue::Low); } pub fn set_phi1_rising_edge(&mut self) { - self.component.set_clock_values(PinValue::High, PinValue::Low); + self.component + .set_clock_values(PinValue::High, PinValue::Low); } pub fn set_phi1_falling_edge(&mut self) { - self.component.set_clock_values(PinValue::Low, PinValue::Low); + self.component + .set_clock_values(PinValue::Low, PinValue::Low); } pub fn set_phi2_rising_edge(&mut self) { - self.component.set_clock_values(PinValue::Low, PinValue::High); + self.component + .set_clock_values(PinValue::Low, PinValue::High); } pub fn set_phi2_falling_edge(&mut self) { - self.component.set_clock_values(PinValue::Low, PinValue::Low); + self.component + .set_clock_values(PinValue::Low, PinValue::Low); } pub fn set_data_bus_value(&mut self, value: u8) { @@ -355,7 +372,7 @@ impl MockScenario { self.time_provider.advance(duration); } - pub fn set_access_time(&mut self, duration: Duration) { + pub fn set_access_time(&mut self, _duration: Duration) { // This would need to be implemented in the actual component // For now, it's a placeholder } @@ -418,4 +435,4 @@ pub mod proptest_helpers { pub fn arb_duration() -> impl Strategy { (0u64..1_000_000_000u64).prop_map(Duration::from_nanos) } -} \ No newline at end of file +} diff --git a/tests/property_based_tests.rs b/tests/property_based_tests.rs index 86aa9fd..f5f3845 100644 --- a/tests/property_based_tests.rs +++ b/tests/property_based_tests.rs @@ -3,10 +3,10 @@ //! These tests use property-based testing to verify the correctness //! of state machine behavior, timing properties, and system invariants. -use rusty_emu::components::common::intel_400x::*; -use rusty_emu::pin::PinValue; use crate::mocks::*; use proptest::prelude::*; +use rusty_emu::components::common::intel_400x::*; +use rusty_emu::pin::PinValue; use std::time::{Duration, Instant}; #[cfg(test)] @@ -100,7 +100,7 @@ mod state_machine_properties { (0x01, 0x23, 0x0123), ]; - for (high, low, expected) in test_cases { + for (high, low, _expected) in test_cases { scenario.component.set_address_high_nibble(Some(high)); scenario.component.set_address_low_nibble(Some(low)); scenario.component.set_full_address_ready(true); @@ -176,7 +176,7 @@ mod state_machine_properties { // Test duration properties prop_assert!(duration.as_nanos() >= 0); - prop_assert_eq!(duration.as_nanos(), duration_nanos as u128); + prop_assert_eq!(duration.as_nanos(), duration_nanos); // Test that our timing constants are reasonable prop_assert!(TimingConstants::DEFAULT_ACCESS_TIME > Duration::from_nanos(0)); @@ -222,7 +222,10 @@ mod timing_invariant_tests { if idle { assert_eq!(active_count, 0, "Idle state should not have active flags"); } else { - assert!(active_count > 0, "Non-idle state should have at least one active flag"); + assert!( + active_count > 0, + "Non-idle state should have at least one active flag" + ); } } } @@ -265,11 +268,7 @@ mod timing_invariant_tests { let scenario = MockScenario::new("TestPinConsistency"); // Test that pin states are consistent - let test_values = vec![ - PinValue::Low, - PinValue::High, - PinValue::HighZ, - ]; + let test_values = vec![PinValue::Low, PinValue::High, PinValue::HighZ]; for value in test_values { scenario.component.set_pin_value("D0", value); @@ -278,7 +277,7 @@ mod timing_invariant_tests { scenario.component.set_pin_value("PHI1", value); assert_eq!(scenario.component.get_pin_value("PHI1"), Some(value)); - scenario.set_pin_value("SYNC", value); + scenario.component.set_pin_value("SYNC", value); assert_eq!(scenario.component.get_pin_value("SYNC"), Some(value)); } } @@ -389,35 +388,43 @@ mod concurrency_safety_tests { let scenario = Arc::new(MockScenario::new("TestThreadSafety")); // Test that our mock can handle concurrent access patterns - let handles: Vec<_> = (0..4).map(|i| { - let scenario_clone: std::sync::Arc = Arc::clone(&scenario); - thread::spawn(move || { - // Each thread tests different aspects - match i { - 0 => { - // Note: This would need proper Arc> handling for thread safety - // For now, we'll skip the mutable operation in this test - // Note: Thread safety testing would require proper Arc> handling + let handles: Vec<_> = (0..4) + .map(|i| { + let scenario_clone: Arc = Arc::clone(&scenario); + thread::spawn(move || { + // Each thread tests different aspects + match i { + 0 => { + // Note: This would need proper Arc> handling for thread safety + // For now, we'll skip the mutable operation in this test + // Note: Thread safety testing would require proper Arc> handling + } + 1 => { + scenario_clone.component.set_pin_value("D0", PinValue::High); + assert_eq!( + scenario_clone.component.get_pin_value("D0"), + Some(PinValue::High) + ); + } + 2 => { + scenario_clone.component.set_address_high_nibble(Some(0x12)); + assert_eq!( + scenario_clone.component.get_address_high_nibble(), + Some(0x12) + ); + } + 3 => { + scenario_clone.advance_time(Duration::from_nanos(100)); + } + _ => unreachable!(), } - 1 => { - scenario_clone.component.set_pin_value("D0", PinValue::High); - assert_eq!(scenario_clone.component.get_pin_value("D0"), Some(PinValue::High)); - } - 2 => { - scenario_clone.component.set_address_high_nibble(Some(0x12)); - assert_eq!(scenario_clone.component.get_address_high_nibble(), Some(0x12)); - } - 3 => { - scenario_clone.advance_time(Duration::from_nanos(100)); - } - _ => unreachable!(), - } + }) }) - }).collect(); + .collect(); // Wait for all threads to complete for handle in handles { handle.join().expect("Thread should complete successfully"); } } -} \ No newline at end of file +} diff --git a/tests/simple_test.rs b/tests/simple_test.rs index 3d80a16..104bda6 100644 --- a/tests/simple_test.rs +++ b/tests/simple_test.rs @@ -188,4 +188,4 @@ mod simple_tests { println!("=== Timing configuration tests completed ==="); } -} \ No newline at end of file +} diff --git a/tests/working_test.rs b/tests/working_test.rs index 7076467..f7b8851 100644 --- a/tests/working_test.rs +++ b/tests/working_test.rs @@ -200,4 +200,4 @@ mod working_tests { println!("✓ Error handling works correctly"); } -} \ No newline at end of file +}