Overhaul the Intel 4004 instruction set to match the real opcode map, adding missing instructions (NOP, FIM, FIN, JIN, ISZ, KBP, DCL, WR0-3, RD0-3) and refining two-byte instruction handling with proper M1/M2 data phases. Rewrite the CPU fetch-execute cycle to correctly process two-byte opcodes across separate clock cycles. Improve Intel 4001 ROM timing with M1/M2 nibble phase tracking and an A3 wait counter so the ROM drives data only when the CPU expects it. Fix state-machine transition helpers in Intel 4002 and 4003 to capture the old state before processing events, preventing incorrect exit/entry callbacks. Add non-propagating pin driver methods to prevent deadlocks caused by bidirectional pin connections during signal propagation. Add Fibonacci integration test and opcode fix plan document. Update all README files to reflect the new instruction set.
15 KiB
Intel 400x Common Functionality
This module provides shared functionality for all Intel 400x series chips (4001, 4002, 4003, 4004) through a unified state machine architecture. 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)
- Common timing constraints and access patterns
- Similar pin configurations and control signals
- Unified state machine patterns for all operations
Rather than duplicating this code in each component, it's centralized here for maintainability and consistency.
Unified State Machine Architecture
Overview
The Intel 400x series now uses a unified state machine approach that replaces individual chip state machines with a single, extensible framework. This approach provides several benefits:
- Consistency: All components follow the same basic state transition patterns
- Maintainability: Common logic is centralized in the state machine engine
- Extensibility: New chip types can be added by implementing the
Intel400xComponentBehaviortrait - Testability: Components can be tested independently with mock behaviors
Key Components
Intel400xComponentBehavior Trait
This trait defines the interface that all Intel 400x components must implement to work with the unified state machine architecture. It allows each component to provide its own specific behavior while still following the common state machine framework.
pub trait Intel400xComponentBehavior {
fn get_component_type(&self) -> Intel400xComponentType;
fn on_state_entry(&mut self, state: Intel400xUnifiedState);
fn on_state_exit(&mut self, state: Intel400xUnifiedState);
fn handle_component_event(&mut self, event: Intel400xEvent) -> bool;
fn execute_state_logic(&mut self, state: Intel400xUnifiedState);
fn responds_to_address(&self) -> bool;
}
Intel400xComponentType
Enumeration that identifies the specific type of Intel 400x component being modeled:
pub enum Intel400xComponentType {
Intel4001, // ROM (Read-Only Memory)
Intel4002, // RAM (Random Access Memory)
Intel4003, // Shift Register
Intel4004, // CPU (Central Processing Unit)
}
Intel400xUnifiedState
Enumeration representing all possible states for any Intel 400x component in the unified state machine:
pub enum Intel400xUnifiedState {
// Common states (shared across all components)
Idle,
AddressPhase,
WaitLatency,
DriveData,
// Component-specific states
ReadData, // Intel 4002 (RAM) specific
WriteData, // Intel 4002 (RAM) specific
OutputPort, // Intel 4002 (RAM) specific
ShiftData, // Intel 4003 (Shift Register) specific
LoadData, // Intel 4003 (Shift Register) specific
FetchByte1, // Intel 4004 (CPU) specific — fetching first byte
FetchByte2, // Intel 4004 (CPU) specific — fetching second byte of 2-byte instruction
Execute, // Intel 4004 (CPU) specific — executing decoded instruction
}
Intel400xEvent
Enumeration representing events that can trigger state transitions:
pub enum Intel400xEvent {
ClockPhi1Rising,
ClockPhi1Falling,
ClockPhi2Rising,
ClockPhi2Falling,
SyncActive,
SyncInactive,
ChipSelect,
ChipDeselect,
ResetActive,
ResetInactive,
AddressComplete,
LatencyElapsed,
DataReady,
OperationComplete,
}
Intel400xStateMachine
The core state machine engine that manages state transitions and coordinates with component behaviors:
pub struct Intel400xStateMachine {
component_type: Intel400xComponentType,
current_state: Intel400xUnifiedState,
previous_state: Intel400xUnifiedState,
address_high_nibble: Option<u8>,
address_low_nibble: Option<u8>,
address_latch_time: Option<Instant>,
access_time: Duration,
}
Trait-Based Design
The common functionality is organized around several key traits:
Intel400xClockHandling - Clock edge detection and timing
Intel400xDataBus - Data bus read/write operations
Intel400xAddressHandling - Address latching and assembly
Intel400xControlPins - Control signal reading
Intel400xResetHandling - Reset signal handling
Intel400xTimingState - Memory operation state management
Core Traits
Intel400xClockHandling
Handles the two-phase clock system used by all 400x components:
pub trait Intel400xClockHandling {
fn get_base(&self) -> &BaseComponent;
// Clock edge detection
fn is_phi1_rising_edge(&self, prev_phi1: PinValue) -> bool;
fn is_phi1_falling_edge(&self, prev_phi1: PinValue) -> bool;
fn is_phi2_rising_edge(&self, prev_phi2: PinValue) -> bool;
fn is_phi2_falling_edge(&self, prev_phi2: PinValue) -> bool;
// Clock state management
fn update_clock_states(&self, prev_phi1: &mut PinValue, prev_phi2: &mut PinValue);
}
Key Features:
- Edge detection for both clock phases
- Previous state tracking for transition detection
- Thread-safe pin access
Intel400xDataBus
Manages the 4-bit multiplexed address/data bus:
pub trait Intel400xDataBus {
fn get_base(&self) -> &BaseComponent;
// Bus operations
fn read_data_bus(&self) -> u8; // Read 4-bit value from D0-D3
fn write_data_bus(&self, data: u8); // Drive 4-bit value on D0-D3
fn tri_state_data_bus(&self); // Set bus to high impedance
}
Key Features:
- Safe pin access with error handling
- Driver name tracking for debugging
- Bus contention avoidance
Intel400xAddressHandling
Handles the two-phase addressing used by 400x components:
pub trait Intel400xAddressHandling {
fn get_base(&self) -> &BaseComponent;
// Address operations
fn assemble_full_address(&self, high_nibble: Option<u8>, low_nibble: Option<u8>) -> Option<u16>;
fn handle_address_latching(&self, nibble: u8, ...); // Complex latching logic
fn handle_latency_wait(&self, address_latch_time: &Option<Instant>, access_time: Duration) -> bool;
}
Key Features:
- Two-phase address latching (high nibble, then low nibble)
- Timing-aware address assembly
- Access time validation
Intel400xControlPins
Reads control signals common to all 400x components:
pub trait Intel400xControlPins {
fn get_base(&self) -> &BaseComponent;
// Control signal reading
fn read_sync_pin(&self) -> bool; // SYNC signal state
fn read_cm_rom_pin(&self) -> bool; // CM-ROM chip select
fn read_reset_pin(&self) -> bool; // RESET signal state
}
Key Features:
- Safe pin reading with fallback values
- Consistent pin naming across components
Intel400xResetHandling
Provides reset functionality for all 400x components:
pub trait Intel400xResetHandling {
fn get_base(&self) -> &BaseComponent;
// Reset operations
fn handle_reset(&mut self, reset_pin_name: &str) -> bool;
fn perform_reset(&mut self); // Component-specific reset logic
}
Key Features:
- System-wide reset signal handling
- Component-specific reset behaviors
Intel400xTimingState
Manages memory operation timing and state machines:
pub trait Intel400xTimingState {
// State management
fn get_timing_state(&self) -> MemoryState;
fn set_timing_state(&mut self, state: MemoryState);
// Address tracking
fn get_address_latch_time(&self) -> Option<Instant>;
fn set_address_latch_time(&mut self, time: Option<Instant>);
// Address components
fn get_full_address_ready(&self) -> bool;
fn get_address_high_nibble(&self) -> Option<u8>;
fn get_address_low_nibble(&self) -> Option<u8>;
// Timing configuration
fn get_access_time(&self) -> Duration;
}
Key Features:
- Unified state machine interface
- Timing constraint management
- Address tracking and validation
State Machine Operation
State Transition Logic
The unified state machine operates by:
- Receiving events from the component or external sources
- Determining the next state based on current state and event
- Calling appropriate state entry/exit methods on the component behavior
- Executing state-specific logic through the component behavior
Common State Transitions
- Idle → AddressPhase: Triggered by chip select when component responds to address
- AddressPhase → WaitLatency: After both address nibbles are latched
- WaitLatency → Component-Specific State: After access time has elapsed
- Component-Specific State → Idle: When operation completes
Component-Specific Behaviors
- Intel 4001 (ROM): Transitions to DriveData to provide read-only data
- Intel 4002 (RAM): Transitions to ReadData, WriteData, or OutputPort based on operation
- Intel 4003 (Shift Register): Ignores address transmission, goes directly to ShiftData
- Intel 4004 (CPU): Uses FetchByte1, FetchByte2, and Execute states for instruction processing
Timing Constants
Standard Timing Values
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
}
Usage in Components
Each component type uses appropriate timing constants:
- ROM (4001): 500ns typical access time
- RAM (4002): 500ns access time with refresh overhead
- Shift Register (4003): 200ns fast access time
- CPU (4004): Internal operation timing
Memory Operation States
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MemoryState {
Idle, // No memory operation in progress
AddressPhase, // Currently latching address nibbles
WaitLatency, // Address latched, waiting for access time
DriveData, // Latency elapsed, driving data on bus
}
Utility Functions
Pin Operations
pub mod utils {
// Safe pin operations with error handling
pub fn is_pin_high(pin: &Arc<Mutex<Pin>>) -> bool;
pub fn is_pin_high_z(pin: &Arc<Mutex<Pin>>) -> bool;
pub fn read_pin_safe(pin: &Arc<Mutex<Pin>>, default: PinValue) -> PinValue;
pub fn set_pin_driver_safe(pin: &Arc<Mutex<Pin>>, driver_name: String, value: PinValue);
// Component naming and identification
pub fn create_driver_name(component_name: &str, suffix: &str) -> String;
}
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
Usage Examples
Implementing the Intel400xComponentBehavior Trait
struct MyComponent {
// Component fields...
state_machine: Intel400xStateMachine,
}
impl Intel400xComponentBehavior for MyComponent {
fn get_component_type(&self) -> Intel400xComponentType {
Intel400xComponentType::Intel4001 // Example type
}
fn on_state_entry(&mut self, state: Intel400xUnifiedState) {
match state {
Intel400xUnifiedState::AddressPhase => {
// Prepare for address latching
}
Intel400xUnifiedState::DriveData => {
// Prepare for data transfer
}
_ => {}
}
}
// ... implement other methods
}
Using the Unified State Machine
// Create a state machine for an Intel 4001 ROM chip
let mut state_machine = Intel400xStateMachine::new(
Intel400xComponentType::Intel4001,
Duration::from_nanos(500)
);
// Process a chip select event
let new_state = process_event_direct(
&mut state_machine,
Intel400xEvent::ChipSelect
);
assert_eq!(new_state, Intel400xUnifiedState::AddressPhase);
Testing
Comprehensive Test Suite
The unified state machine is thoroughly tested with:
- Unit Tests: Test individual functions and algorithms
- Mock Tests: Test with controlled hardware inputs
- Property Tests: Verify behavioral properties
- Integration Tests: Test with real component implementations
Testable Areas
- State machine transitions
- Address latching algorithms
- Component-specific behaviors
- Clock edge detection accuracy
- Data bus bit operations
- Pin operation safety
Benefits of the Unified Approach
- Code Reuse: Common state transition logic is shared across all components
- Consistency: All components follow the same timing and state patterns
- Maintainability: Changes to common behavior only need to be made in one place
- Extensibility: New chip types can be added without modifying the core state machine
- Testability: The state machine can be tested independently of specific components
Hardware Accuracy
Timing Considerations
The unified state machine implements hardware-accurate timing where possible:
- Clock Edge Detection: Proper Φ1/Φ2 edge detection
- Address Latching: Two-phase address capture
- Access Time Simulation: Configurable access latencies
- Bus State Management: Proper drive/tri-state timing
Limitations
- Rust Timing: Limited by Rust's timing resolution
- Threading: Simulation runs in single thread vs. hardware parallelism
- Signal Propagation: Simplified signal timing for simulation
Future Enhancements
Planned Improvements
-
Enhanced Timing Simulation
- More accurate sub-nanosecond timing
- Better clock phase relationship modeling
- Improved signal propagation delays
-
Extended Component Support
- Support for additional Intel 400x variants
- Enhanced I/O device support
- Better peripheral modeling
-
Testing Improvements
- More sophisticated mock scenarios
- Better timing verification tools
- Enhanced debugging capabilities
Extension Points
The trait-based design makes it easy to:
- Add New Components: Implement the required traits
- Enhance Timing: Modify timing constants and logic
- Improve Testing: Add new mock scenarios and test utilities
- Extend Functionality: Add new common behaviors as needed
Dependencies
The common functionality depends on:
- Base Component System: For pin management and lifecycle
- Pin Abstraction: For signal state management
- Timing Utilities: For duration and instant handling
- Error Handling: For robust operation
Performance
Optimization Features
- Efficient Pin Access: Minimized lock contention
- Fast State Transitions: Optimized state machine
- Minimal Allocations: Reuse of common data structures
- Lock-Free Algorithms: Where possible for performance
Memory Usage
- Small Footprint: Minimal memory overhead per component
- Shared Code: No duplication across components
- Efficient State Storage: Compact state representation
This unified state machine architecture provides a solid foundation for all Intel 400x components while maintaining high performance and hardware accuracy.