Created factory for system creation.
Systems are now defined in JSON rather than hard-coded
This commit is contained in:
parent
c039e9069a
commit
6f3295dc5e
31 changed files with 1610 additions and 1709 deletions
|
|
@ -1,6 +1,7 @@
|
|||
# Hardware Components
|
||||
|
||||
This directory contains implementations of various hardware components used in the Intel 4004 microprocessor system and other architectures.
|
||||
This directory contains implementations of various hardware components used in the Intel 4004 microprocessor system and
|
||||
other architectures.
|
||||
|
||||
## Component Architecture
|
||||
|
||||
|
|
@ -16,22 +17,25 @@ All components implement the base `Component` trait defined in `src/component.rs
|
|||
### CPU Components (`cpu/`)
|
||||
|
||||
#### Intel 4004 CPU
|
||||
|
||||
- **File**: `intel_4004.rs`
|
||||
- **Features**:
|
||||
- Complete Intel 4004 instruction set implementation
|
||||
- 4-bit data processing
|
||||
- 12-bit address space
|
||||
- Two-phase clock operation (Φ1, Φ2)
|
||||
- 16 general-purpose registers (8 index, 8 main)
|
||||
- Stack-based subroutine calls
|
||||
- Complete Intel 4004 instruction set implementation
|
||||
- 4-bit data processing
|
||||
- 12-bit address space
|
||||
- Two-phase clock operation (Φ1, Φ2)
|
||||
- 16 general-purpose registers (8 index, 8 main)
|
||||
- Stack-based subroutine calls
|
||||
- **Status**: Fully implemented with comprehensive testing
|
||||
|
||||
#### MOS 6502 CPU (Placeholder)
|
||||
|
||||
- **File**: `mos_6502.rs`
|
||||
- **Features**: Placeholder for MOS Technology 6502 implementation
|
||||
- **Status**: Not yet implemented
|
||||
|
||||
#### WDC 65C02 CPU (Placeholder)
|
||||
|
||||
- **File**: `wdc_65c02.rs`
|
||||
- **Features**: Placeholder for Western Design Center 65C02 implementation
|
||||
- **Status**: Not yet implemented
|
||||
|
|
@ -39,67 +43,74 @@ All components implement the base `Component` trait defined in `src/component.rs
|
|||
### Memory Components (`memory/`)
|
||||
|
||||
#### Intel 4001 ROM
|
||||
|
||||
- **File**: `intel_4001.rs`
|
||||
- **Features**:
|
||||
- 256 bytes of mask-programmable ROM
|
||||
- 4-bit I/O ports for peripheral interface
|
||||
- Two-phase addressing (8-bit address in two 4-bit cycles)
|
||||
- 500ns typical access time
|
||||
- SYNC signal detection for instruction fetch
|
||||
- 256 bytes of mask-programmable ROM
|
||||
- 4-bit I/O ports for peripheral interface
|
||||
- Two-phase addressing (8-bit address in two 4-bit cycles)
|
||||
- 500ns typical access time
|
||||
- SYNC signal detection for instruction fetch
|
||||
- **Status**: Fully implemented with comprehensive testing
|
||||
|
||||
#### Intel 4002 RAM
|
||||
|
||||
- **File**: `intel_4002.rs`
|
||||
- **Features**:
|
||||
- 320 bits of read/write memory (80 × 4-bit characters)
|
||||
- 4-bit output port for status/display
|
||||
- Banked memory organization (4 banks × 20 characters)
|
||||
- Refresh circuitry for dynamic memory cells
|
||||
- Status character instructions for control
|
||||
- 320 bits of read/write memory (80 × 4-bit characters)
|
||||
- 4-bit output port for status/display
|
||||
- Banked memory organization (4 banks × 20 characters)
|
||||
- Refresh circuitry for dynamic memory cells
|
||||
- Status character instructions for control
|
||||
- **Status**: Implemented with known limitations
|
||||
|
||||
#### Intel 4003 Shift Register
|
||||
|
||||
- **File**: `intel_4003.rs`
|
||||
- **Features**:
|
||||
- 10-bit static shift register
|
||||
- Serial input/output capability
|
||||
- Parallel input from data bus
|
||||
- 200ns typical access time
|
||||
- Bidirectional shifting
|
||||
- 10-bit static shift register
|
||||
- Serial input/output capability
|
||||
- Parallel input from data bus
|
||||
- 200ns typical access time
|
||||
- Bidirectional shifting
|
||||
- **Status**: Basic implementation
|
||||
|
||||
#### Generic Memory Components
|
||||
|
||||
- **GenericRAM**: `generic_ram.rs` - Configurable RAM implementation
|
||||
- **GenericROM**: `generic_rom.rs` - Configurable ROM implementation
|
||||
|
||||
### Common Functionality (`common/`)
|
||||
|
||||
#### Intel 400x Series Common Code
|
||||
|
||||
- **File**: `intel_400x.rs`
|
||||
- **Purpose**: Shared functionality for all Intel 400x series chips
|
||||
- **Features**:
|
||||
- Common timing constants and state machines
|
||||
- Clock edge detection and handling
|
||||
- Data bus operations and bit manipulation
|
||||
- Address latching and assembly logic
|
||||
- Control pin reading and management
|
||||
- Reset handling and initialization
|
||||
- Memory operation state machines
|
||||
- Common timing constants and state machines
|
||||
- Clock edge detection and handling
|
||||
- Data bus operations and bit manipulation
|
||||
- Address latching and assembly logic
|
||||
- Control pin reading and management
|
||||
- Reset handling and initialization
|
||||
- Memory operation state machines
|
||||
|
||||
### Clock Components (`clock/`)
|
||||
|
||||
#### Generic Clock Generator
|
||||
|
||||
- **File**: `generic_clock.rs`
|
||||
- **Features**:
|
||||
- Configurable frequency and duty cycle
|
||||
- Multiple output waveforms
|
||||
- Synchronization capabilities
|
||||
- Test pattern generation
|
||||
- Configurable frequency and duty cycle
|
||||
- Multiple output waveforms
|
||||
- Synchronization capabilities
|
||||
- Test pattern generation
|
||||
- **Status**: Basic implementation
|
||||
|
||||
## Component Interface
|
||||
|
||||
### Required Methods
|
||||
|
||||
All components must implement:
|
||||
|
||||
```rust
|
||||
|
|
@ -113,6 +124,7 @@ fn is_running(&self) -> bool; // Execution status
|
|||
```
|
||||
|
||||
### Optional Traits
|
||||
|
||||
Components can implement additional traits for specialized functionality:
|
||||
|
||||
- **RunnableComponent**: Automatic thread spawning
|
||||
|
|
@ -122,12 +134,14 @@ Components can implement additional traits for specialized functionality:
|
|||
## Pin Configuration
|
||||
|
||||
### Standard Pin Types
|
||||
|
||||
- **Data Pins**: `D0-D3` for 4-bit data/address bus
|
||||
- **Clock Pins**: `PHI1`, `PHI2` for two-phase clock
|
||||
- **Control Pins**: `SYNC`, `RESET`, `CM`, `CI` for system control
|
||||
- **I/O Pins**: `IO0-IO3` for peripheral interface
|
||||
|
||||
### Pin Signal States
|
||||
|
||||
- **High**: Logic level 1
|
||||
- **Low**: Logic level 0
|
||||
- **HighZ**: High impedance (tri-state, not driving)
|
||||
|
|
@ -135,6 +149,7 @@ Components can implement additional traits for specialized functionality:
|
|||
## Usage Examples
|
||||
|
||||
### Creating and Using a Component
|
||||
|
||||
```rust
|
||||
use rusty_emu::components::memory::intel_4001::Intel4001;
|
||||
|
||||
|
|
@ -143,38 +158,42 @@ let mut rom = Intel4001::new("ROM1".to_string());
|
|||
|
||||
// Load program data
|
||||
let program = vec![0x12, 0x34, 0x56, 0x78];
|
||||
rom.load_rom_data(program, 0)?;
|
||||
rom.load_rom_data(program, 0) ?;
|
||||
|
||||
// Use in simulation loop
|
||||
while simulation_running() {
|
||||
rom.update(); // Process one clock cycle
|
||||
rom.update(); // Process one clock cycle
|
||||
}
|
||||
```
|
||||
|
||||
### Component Interconnection
|
||||
|
||||
```rust
|
||||
// Components connect through shared pin references
|
||||
let cpu_pin = cpu.get_pin("D0")?;
|
||||
let rom_pin = rom.get_pin("D0")?;
|
||||
let ram_pin = ram.get_pin("D0")?;
|
||||
let cpu_pin = cpu.get_pin("D0") ?;
|
||||
let rom_pin = rom.get_pin("D0") ?;
|
||||
let ram_pin = ram.get_pin("D0") ?;
|
||||
|
||||
// Connect pins together
|
||||
connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
||||
connect_pins(vec![cpu_pin, rom_pin, ram_pin]) ?;
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Located in each component's implementation file
|
||||
- Test individual component behavior in isolation
|
||||
- Focus on state management and pin operations
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Located in `tests/` directory
|
||||
- Test component interactions and system behavior
|
||||
- Verify proper timing and signal propagation
|
||||
|
||||
### Mock-Based Tests
|
||||
|
||||
- Use mock components for controlled testing
|
||||
- Test error conditions and edge cases
|
||||
- Verify behavior with various input patterns
|
||||
|
|
@ -182,6 +201,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
## Development Guidelines
|
||||
|
||||
### Adding New Components
|
||||
|
||||
1. **Implement Core Traits**: Start with `Component` trait implementation
|
||||
2. **Define Pin Configuration**: Specify required pins and their purposes
|
||||
3. **Implement Timing**: Add clock cycle processing in `update()` method
|
||||
|
|
@ -190,6 +210,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
6. **Update Exports**: Add the component to the appropriate module exports
|
||||
|
||||
### Component Design Principles
|
||||
|
||||
- **Single Responsibility**: Each component should have one primary function
|
||||
- **Clear Interfaces**: Well-defined pin configurations and behaviors
|
||||
- **Timing Awareness**: Proper handling of clock cycles and timing constraints
|
||||
|
|
@ -197,6 +218,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
- **Testability**: Design for easy testing and verification
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- **Pin Access**: Minimize lock contention on shared pins
|
||||
- **State Updates**: Efficient state machine transitions
|
||||
- **Memory Layout**: Optimize memory access patterns
|
||||
|
|
@ -205,6 +227,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
## Hardware Accuracy
|
||||
|
||||
### Intel 4004 Series Specifics
|
||||
|
||||
- **Two-Phase Clock**: All operations synchronized to Φ1 and Φ2
|
||||
- **4-Bit Architecture**: Data processed in 4-bit nibbles
|
||||
- **12-Bit Addressing**: 8-bit addresses transferred in two 4-bit cycles
|
||||
|
|
@ -212,6 +235,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
- **Bus Sharing**: Multiple components share the same data bus
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
- Some timing specifications are approximated due to Rust's timing limitations
|
||||
- Pin signal propagation is simplified for simulation performance
|
||||
- Bus contention handling prioritizes correctness over perfect hardware accuracy
|
||||
|
|
@ -220,6 +244,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
## Future Enhancements
|
||||
|
||||
### Planned Components
|
||||
|
||||
- Enhanced Intel 4002 RAM with proper refresh cycles
|
||||
- Intel 4008/4009 I/O expanders
|
||||
- Intel 4269 programmable ROM
|
||||
|
|
@ -227,6 +252,7 @@ connect_pins(vec![cpu_pin, rom_pin, ram_pin])?;
|
|||
- Cycle-accurate instruction execution
|
||||
|
||||
### Architecture Improvements
|
||||
|
||||
- Better pin signal propagation modeling
|
||||
- More sophisticated bus arbitration
|
||||
- Enhanced timing constraint checking
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
# Intel 400x Common Functionality
|
||||
|
||||
This module provides shared functionality for all Intel 400x series chips (4001, 4002, 4003, 4004). It contains common traits, utilities, and behaviors that are used across multiple components.
|
||||
This module provides shared functionality for all Intel 400x series chips (4001, 4002, 4003, 4004). It contains common
|
||||
traits, utilities, and behaviors that are used across multiple components.
|
||||
|
||||
## Purpose
|
||||
|
||||
The Intel 400x series shares many common characteristics:
|
||||
|
||||
- **Two-phase clock operation** (Φ1, Φ2)
|
||||
- **4-bit data bus** with multiplexed address/data
|
||||
- **12-bit address space** (8-bit addresses in two 4-bit cycles)
|
||||
|
|
@ -61,6 +63,7 @@ pub trait Intel400xClockHandling {
|
|||
```
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- Edge detection for both clock phases
|
||||
- Previous state tracking for transition detection
|
||||
- Thread-safe pin access
|
||||
|
|
@ -81,6 +84,7 @@ pub trait Intel400xDataBus {
|
|||
```
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- Safe pin access with error handling
|
||||
- Driver name tracking for debugging
|
||||
- Bus contention avoidance
|
||||
|
|
@ -101,6 +105,7 @@ pub trait Intel400xAddressHandling {
|
|||
```
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- Two-phase address latching (high nibble, then low nibble)
|
||||
- Timing-aware address assembly
|
||||
- Access time validation
|
||||
|
|
@ -121,6 +126,7 @@ pub trait Intel400xControlPins {
|
|||
```
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- Safe pin reading with fallback values
|
||||
- Consistent pin naming across components
|
||||
|
||||
|
|
@ -139,6 +145,7 @@ pub trait Intel400xResetHandling {
|
|||
```
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- System-wide reset signal handling
|
||||
- Component-specific reset behaviors
|
||||
|
||||
|
|
@ -167,6 +174,7 @@ pub trait Intel400xTimingState {
|
|||
```
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- Unified state machine interface
|
||||
- Timing constraint management
|
||||
- Address tracking and validation
|
||||
|
|
@ -243,6 +251,7 @@ pub mod utils {
|
|||
### Error Handling
|
||||
|
||||
All pin operations include proper error handling:
|
||||
|
||||
- **Lock failures**: Return safe default values
|
||||
- **Pin not found**: Return appropriate error messages
|
||||
- **Invalid states**: Graceful degradation
|
||||
|
|
@ -347,19 +356,19 @@ The common code implements hardware-accurate timing where possible:
|
|||
### Planned Improvements
|
||||
|
||||
1. **Enhanced Timing Simulation**
|
||||
- More accurate sub-nanosecond timing
|
||||
- Better clock phase relationship modeling
|
||||
- Improved signal propagation delays
|
||||
- More accurate sub-nanosecond timing
|
||||
- Better clock phase relationship modeling
|
||||
- Improved signal propagation delays
|
||||
|
||||
2. **Extended Component Support**
|
||||
- Support for additional Intel 400x variants
|
||||
- Enhanced I/O device support
|
||||
- Better peripheral modeling
|
||||
- Support for additional Intel 400x variants
|
||||
- Enhanced I/O device support
|
||||
- Better peripheral modeling
|
||||
|
||||
3. **Testing Improvements**
|
||||
- More sophisticated mock scenarios
|
||||
- Better timing verification tools
|
||||
- Enhanced debugging capabilities
|
||||
- More sophisticated mock scenarios
|
||||
- Better timing verification tools
|
||||
- Enhanced debugging capabilities
|
||||
|
||||
### Extension Points
|
||||
|
||||
|
|
@ -394,4 +403,5 @@ The common functionality depends on:
|
|||
- **Shared Code**: No duplication across components
|
||||
- **Efficient State Storage**: Compact state representation
|
||||
|
||||
This common functionality module provides a solid foundation for all Intel 400x components while maintaining high performance and hardware accuracy.
|
||||
This common functionality module provides a solid foundation for all Intel 400x components while maintaining high
|
||||
performance and hardware accuracy.
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
//! This module provides shared timing, clock handling, and bus operations
|
||||
//! used across all Intel 400x family components (4001, 4002, 4003, 4004).
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
// Memory operation state machine states (shared across all 400x chips)
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
|
@ -17,15 +17,14 @@ pub enum MemoryState {
|
|||
DriveData, // Latency elapsed, driving data on bus
|
||||
}
|
||||
|
||||
|
||||
/// Common timing constants for Intel 400x series
|
||||
pub struct TimingConstants;
|
||||
|
||||
impl TimingConstants {
|
||||
pub const DEFAULT_ACCESS_TIME: Duration = Duration::from_nanos(500); // 500ns default
|
||||
pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers
|
||||
pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time
|
||||
pub const DATA_VALID: Duration = Duration::from_nanos(200); // Data valid delay
|
||||
pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers
|
||||
pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time
|
||||
pub const DATA_VALID: Duration = Duration::from_nanos(200); // Data valid delay
|
||||
}
|
||||
|
||||
/// Common clock edge detection and timing functionality
|
||||
|
|
@ -121,7 +120,10 @@ pub trait Intel400xDataBus {
|
|||
} else {
|
||||
PinValue::Low
|
||||
};
|
||||
pin_guard.set_driver(Some(format!("{}_DATA", self.get_base().get_name())), pin_value);
|
||||
pin_guard.set_driver(
|
||||
Some(format!("{}_DATA", self.get_base().get_name())),
|
||||
pin_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +134,10 @@ pub trait Intel400xDataBus {
|
|||
for i in 0..4 {
|
||||
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
pin_guard.set_driver(Some(format!("{}_DATA", self.get_base().get_name())), PinValue::HighZ);
|
||||
pin_guard.set_driver(
|
||||
Some(format!("{}_DATA", self.get_base().get_name())),
|
||||
PinValue::HighZ,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -146,7 +151,11 @@ pub trait Intel400xAddressHandling {
|
|||
/// Assemble complete 8-bit address from high and low nibbles
|
||||
/// Hardware: Intel 4004 provides address in two 4-bit phases
|
||||
/// Format: (high_nibble << 4) | low_nibble
|
||||
fn assemble_full_address(&self, high_nibble: Option<u8>, low_nibble: Option<u8>) -> Option<u16> {
|
||||
fn assemble_full_address(
|
||||
&self,
|
||||
high_nibble: Option<u8>,
|
||||
low_nibble: Option<u8>,
|
||||
) -> Option<u16> {
|
||||
if let (Some(high), Some(low)) = (high_nibble, low_nibble) {
|
||||
Some(((high as u16) << 4) | (low as u16))
|
||||
} else {
|
||||
|
|
@ -163,7 +172,7 @@ pub trait Intel400xAddressHandling {
|
|||
address_low_nibble: &mut Option<u8>,
|
||||
full_address_ready: &mut bool,
|
||||
address_latch_time: &mut Option<Instant>,
|
||||
access_time: Duration,
|
||||
_access_time: Duration,
|
||||
) {
|
||||
if address_high_nibble.is_none() {
|
||||
// First cycle: latch high nibble (bits 7-4)
|
||||
|
|
@ -173,7 +182,9 @@ pub trait Intel400xAddressHandling {
|
|||
*address_low_nibble = Some(nibble);
|
||||
|
||||
// Try to assemble full address
|
||||
if let Some(address) = self.assemble_full_address(*address_high_nibble, *address_low_nibble) {
|
||||
if let Some(_address) =
|
||||
self.assemble_full_address(*address_high_nibble, *address_low_nibble)
|
||||
{
|
||||
*full_address_ready = true;
|
||||
*address_latch_time = Some(Instant::now());
|
||||
|
||||
|
|
@ -186,7 +197,11 @@ pub trait Intel400xAddressHandling {
|
|||
|
||||
/// Handle latency timing during wait state
|
||||
/// Returns: true if latency has elapsed
|
||||
fn handle_latency_wait(&self, address_latch_time: &Option<Instant>, access_time: Duration) -> bool {
|
||||
fn handle_latency_wait(
|
||||
&self,
|
||||
address_latch_time: &Option<Instant>,
|
||||
access_time: Duration,
|
||||
) -> bool {
|
||||
if let Some(latch_time) = address_latch_time {
|
||||
latch_time.elapsed() >= access_time
|
||||
} else {
|
||||
|
|
@ -214,7 +229,8 @@ pub trait Intel400xControlPins {
|
|||
|
||||
/// Read CM-ROM pin state
|
||||
fn read_cm_rom_pin(&self) -> bool {
|
||||
if let Ok(pin) = self.get_base().get_pin("CM") { // Note: CM pin name varies by component
|
||||
if let Ok(pin) = self.get_base().get_pin("CM") {
|
||||
// Note: CM pin name varies by component
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
pin_guard.read() == PinValue::High
|
||||
} else {
|
||||
|
|
@ -413,4 +429,4 @@ pub mod utils {
|
|||
}
|
||||
|
||||
// Re-export commonly used items for convenience
|
||||
pub use utils::*;
|
||||
pub use utils::*;
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
pub mod intel_400x;
|
||||
|
||||
// Re-export commonly used items for convenience
|
||||
pub use intel_400x::*;
|
||||
pub use intel_400x::*;
|
||||
|
|
|
|||
|
|
@ -40,26 +40,26 @@ enum MemoryState {
|
|||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum Instruction {
|
||||
// Data Transfer Instructions (8)
|
||||
Ldm(u8), // Load accumulator immediate (LDM #)
|
||||
Ld(u8), // Load accumulator from register (LD R)
|
||||
Xch(u8), // Exchange accumulator with register (XCH R)
|
||||
Add(u8), // Add register to accumulator (ADD R)
|
||||
Sub(u8), // Subtract register from accumulator (SUB R)
|
||||
Inc(u8), // Increment register (INC R)
|
||||
Bbl(u8), // Branch back and load (BBL #)
|
||||
Jun(u16), // Jump unconditional (JUN addr)
|
||||
Ldm(u8), // Load accumulator immediate (LDM #)
|
||||
Ld(u8), // Load accumulator from register (LD R)
|
||||
Xch(u8), // Exchange accumulator with register (XCH R)
|
||||
Add(u8), // Add register to accumulator (ADD R)
|
||||
Sub(u8), // Subtract register from accumulator (SUB R)
|
||||
Inc(u8), // Increment register (INC R)
|
||||
Bbl(u8), // Branch back and load (BBL #)
|
||||
Jun(u16), // Jump unconditional (JUN addr)
|
||||
|
||||
// Arithmetic Instructions (4)
|
||||
AddC(u8), // Add register with carry (ADC R)
|
||||
SubC(u8), // Subtract register with carry (SBC R)
|
||||
Dad(u8), // Decimal add register (DAD R)
|
||||
Daa, // Decimal adjust accumulator (DAA)
|
||||
AddC(u8), // Add register with carry (ADC R)
|
||||
SubC(u8), // Subtract register with carry (SBC R)
|
||||
Dad(u8), // Decimal add register (DAD R)
|
||||
Daa, // Decimal adjust accumulator (DAA)
|
||||
|
||||
// Logic Instructions (4)
|
||||
Ral, // Rotate left (RAL)
|
||||
Rar, // Rotate right (RAR)
|
||||
Tcc, // Transmit carry clear (TCC)
|
||||
Tcs, // Transmit carry set (TCS)
|
||||
Ral, // Rotate left (RAL)
|
||||
Rar, // Rotate right (RAR)
|
||||
Tcc, // Transmit carry clear (TCC)
|
||||
Tcs, // Transmit carry set (TCS)
|
||||
|
||||
// Control Transfer Instructions (8)
|
||||
Jcn(u8, u16), // Jump conditional (JCN condition, addr)
|
||||
|
|
@ -68,25 +68,25 @@ enum Instruction {
|
|||
JntInvert(u16), // Jump on test inverted (JNT addr) - wait instruction
|
||||
|
||||
// Machine Instruction (1)
|
||||
Src(u8), // Send register control (SRC R)
|
||||
Src(u8), // Send register control (SRC R)
|
||||
|
||||
// Input/Output and RAM Instructions (8)
|
||||
Wrm, // Write accumulator to RAM (WRM)
|
||||
Wmp, // Write memory pointer (WMP)
|
||||
Wrr, // Write ROM port and register (WRR)
|
||||
Wpm, // Write program memory (WPM)
|
||||
Adm, // Add from memory (ADM)
|
||||
Sbm, // Subtract from memory (SBM)
|
||||
Rdm, // Read memory (RDM)
|
||||
Rdr, // Read ROM port and register (RDR)
|
||||
Wrm, // Write accumulator to RAM (WRM)
|
||||
Wmp, // Write memory pointer (WMP)
|
||||
Wrr, // Write ROM port and register (WRR)
|
||||
Wpm, // Write program memory (WPM)
|
||||
Adm, // Add from memory (ADM)
|
||||
Sbm, // Subtract from memory (SBM)
|
||||
Rdm, // Read memory (RDM)
|
||||
Rdr, // Read ROM port and register (RDR)
|
||||
|
||||
// Accumulator Group Instructions (8)
|
||||
Clb, // Clear both (CLB)
|
||||
Clc, // Clear carry (CLC)
|
||||
Cmc, // Complement carry (CMC)
|
||||
Stc, // Set carry (STC)
|
||||
Cma, // Complement accumulator (CMA)
|
||||
Iac, // Increment accumulator (IAC)
|
||||
Clb, // Clear both (CLB)
|
||||
Clc, // Clear carry (CLC)
|
||||
Cmc, // Complement carry (CMC)
|
||||
Stc, // Set carry (STC)
|
||||
Cma, // Complement accumulator (CMA)
|
||||
Iac, // Increment accumulator (IAC)
|
||||
// Note: CMC and RAL are already defined above
|
||||
|
||||
// Invalid instruction
|
||||
|
|
@ -124,19 +124,19 @@ pub struct Intel4004 {
|
|||
ram_bank: u8, // Currently selected RAM bank (0-7)
|
||||
|
||||
// Two-phase clock state tracking
|
||||
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
|
||||
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
|
||||
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
|
||||
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
|
||||
|
||||
// Memory operation state machine
|
||||
memory_state: MemoryState, // Current state of memory operation
|
||||
address_high_nibble: Option<u8>, // High nibble of 8-bit address
|
||||
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
|
||||
full_address_ready: bool, // Whether complete address is assembled
|
||||
memory_state: MemoryState, // Current state of memory operation
|
||||
address_high_nibble: Option<u8>, // High nibble of 8-bit address
|
||||
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
|
||||
full_address_ready: bool, // Whether complete address is assembled
|
||||
|
||||
// Instruction execution state
|
||||
current_op: Instruction, // Currently decoded instruction
|
||||
operand_latch: u8, // Latched operand for multi-byte instructions
|
||||
jump_address: Option<u16>, // Jump target address for conditional jumps
|
||||
current_op: Instruction, // Currently decoded instruction
|
||||
operand_latch: u8, // Latched operand for multi-byte instructions
|
||||
jump_address: Option<u16>, // Jump target address for conditional jumps
|
||||
|
||||
// Timing and synchronization
|
||||
address_latch_time: Option<Instant>, // Timestamp when address was latched
|
||||
|
|
@ -150,13 +150,13 @@ impl Intel4004 {
|
|||
pub fn new(name: String, clock_speed: f64) -> Self {
|
||||
let pin_names = vec![
|
||||
"D0", "D1", "D2", "D3", // Data bus pins
|
||||
"SYNC", // Sync signal
|
||||
"CM_ROM", // ROM chip select
|
||||
"CM_RAM", // RAM chip select
|
||||
"TEST", // Test pin
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
"SYNC", // Sync signal
|
||||
"CM_ROM", // ROM chip select
|
||||
"CM_RAM", // RAM chip select
|
||||
"TEST", // Test pin
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
];
|
||||
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
|
@ -462,7 +462,8 @@ impl Intel4004 {
|
|||
/// Hardware: RESET pin clears all internal state and tri-states outputs
|
||||
fn handle_reset(&mut self) {
|
||||
let (_, _, _, test) = self.read_control_pins();
|
||||
if test { // Note: Using TEST pin as RESET for now - should be RESET pin
|
||||
if test {
|
||||
// Note: Using TEST pin as RESET for now - should be RESET pin
|
||||
// RESET is high - clear all internal state
|
||||
self.accumulator = 0;
|
||||
self.carry = false;
|
||||
|
|
@ -575,7 +576,7 @@ impl Intel4004 {
|
|||
fn assemble_full_address(&mut self) {
|
||||
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
|
||||
// Assemble 8-bit address: (high << 4) | low
|
||||
self.address_latch = ((high as u8) << 4) | (low as u8);
|
||||
self.address_latch = (high << 4) | low;
|
||||
self.full_address_ready = true;
|
||||
self.address_latch_time = Some(Instant::now());
|
||||
|
||||
|
|
@ -646,7 +647,7 @@ impl Intel4004 {
|
|||
0x00..=0x0F => {
|
||||
let reg = opcode & 0x0F;
|
||||
if opcode < 0x08 {
|
||||
Instruction::Ld(reg) // LD R
|
||||
Instruction::Ld(reg) // LD R
|
||||
} else {
|
||||
Instruction::Xch(reg) // XCH R
|
||||
}
|
||||
|
|
@ -656,17 +657,17 @@ impl Intel4004 {
|
|||
0x10..=0x1F => {
|
||||
let reg = opcode & 0x0F;
|
||||
if opcode < 0x18 {
|
||||
Instruction::Add(reg) // ADD R
|
||||
Instruction::Add(reg) // ADD R
|
||||
} else {
|
||||
Instruction::Sub(reg) // SUB R
|
||||
Instruction::Sub(reg) // SUB R
|
||||
}
|
||||
}
|
||||
|
||||
// Logic Instructions (0x20-0x2F)
|
||||
0x20..=0x2F => {
|
||||
match opcode {
|
||||
0x20..=0x27 => Instruction::AddC(opcode & 0x0F), // ADC R
|
||||
0x28..=0x2F => Instruction::SubC(opcode & 0x0F), // SBC R
|
||||
0x20..=0x27 => Instruction::AddC(opcode & 0x0F), // ADC R
|
||||
0x28..=0x2F => Instruction::SubC(opcode & 0x0F), // SBC R
|
||||
_ => Instruction::Invalid,
|
||||
}
|
||||
}
|
||||
|
|
@ -674,10 +675,10 @@ impl Intel4004 {
|
|||
// Control Transfer Instructions (0x30-0x3F)
|
||||
0x30..=0x3F => {
|
||||
match opcode {
|
||||
0x30..=0x33 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x34..=0x37 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x38..=0x3B => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x3C..=0x3F => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x30..=0x33 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x34..=0x37 => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x38..=0x3B => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
0x3C..=0x3F => Instruction::Jcn(opcode & 0x0F, 0), // JCN condition
|
||||
_ => Instruction::Invalid,
|
||||
}
|
||||
}
|
||||
|
|
@ -685,24 +686,23 @@ impl Intel4004 {
|
|||
// Immediate Instructions (0x40-0x4F)
|
||||
0x40..=0x4F => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Ldm(reg) // LDM #
|
||||
Instruction::Ldm(reg) // LDM #
|
||||
}
|
||||
|
||||
// I/O and RAM Instructions (0x50-0x5F)
|
||||
0x50..=0x5F => {
|
||||
match opcode {
|
||||
0x50..=0x57 => Instruction::Wrm, // WRM
|
||||
0x58..=0x5F => Instruction::Wmp, // WMP
|
||||
0x50..=0x57 => Instruction::Wrm, // WRM
|
||||
0x58..=0x5F => Instruction::Wmp, // WMP
|
||||
_ => Instruction::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
// Register Reference Instructions (0x60-0x6F)
|
||||
0x60..=0x6F => {
|
||||
let reg = opcode & 0x0F;
|
||||
match opcode {
|
||||
0x60..=0x67 => Instruction::Wrr, // WRR
|
||||
0x68..=0x6F => Instruction::Wpm, // WPM
|
||||
0x60..=0x67 => Instruction::Wrr, // WRR
|
||||
0x68..=0x6F => Instruction::Wpm, // WPM
|
||||
_ => Instruction::Invalid,
|
||||
}
|
||||
}
|
||||
|
|
@ -710,22 +710,22 @@ impl Intel4004 {
|
|||
// Accumulator Group Instructions (0x70-0x7F)
|
||||
0x70..=0x7F => {
|
||||
match opcode {
|
||||
0x70 => Instruction::Adm, // ADM
|
||||
0x71 => Instruction::Sbm, // SBM
|
||||
0x72 => Instruction::Clb, // CLB
|
||||
0x73 => Instruction::Clc, // CLC
|
||||
0x74 => Instruction::Cmc, // CMC
|
||||
0x75 => Instruction::Stc, // STC
|
||||
0x76 => Instruction::Cma, // CMA
|
||||
0x77 => Instruction::Iac, // IAC
|
||||
0x78 => Instruction::Rdm, // RDM
|
||||
0x79 => Instruction::Rdr, // RDR
|
||||
0x7A => Instruction::Ral, // RAL
|
||||
0x7B => Instruction::Rar, // RAR
|
||||
0x7C => Instruction::Tcc, // TCC
|
||||
0x7D => Instruction::Tcs, // TCS
|
||||
0x7E => Instruction::Daa, // DAA
|
||||
0x7F => Instruction::Tcs, // TCS (duplicate in some docs)
|
||||
0x70 => Instruction::Adm, // ADM
|
||||
0x71 => Instruction::Sbm, // SBM
|
||||
0x72 => Instruction::Clb, // CLB
|
||||
0x73 => Instruction::Clc, // CLC
|
||||
0x74 => Instruction::Cmc, // CMC
|
||||
0x75 => Instruction::Stc, // STC
|
||||
0x76 => Instruction::Cma, // CMA
|
||||
0x77 => Instruction::Iac, // IAC
|
||||
0x78 => Instruction::Rdm, // RDM
|
||||
0x79 => Instruction::Rdr, // RDR
|
||||
0x7A => Instruction::Ral, // RAL
|
||||
0x7B => Instruction::Rar, // RAR
|
||||
0x7C => Instruction::Tcc, // TCC
|
||||
0x7D => Instruction::Tcs, // TCS
|
||||
0x7E => Instruction::Daa, // DAA
|
||||
0x7F => Instruction::Tcs, // TCS (duplicate in some docs)
|
||||
_ => Instruction::Invalid,
|
||||
}
|
||||
}
|
||||
|
|
@ -733,62 +733,62 @@ impl Intel4004 {
|
|||
// Jump Instructions (0x80-0x8F)
|
||||
0x80..=0x8F => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R
|
||||
Instruction::Src(reg) // SRC R
|
||||
}
|
||||
|
||||
// Jump Instructions (0x90-0x9F)
|
||||
0x90..=0x9F => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
}
|
||||
|
||||
// Jump Instructions (0xA0-0xAF)
|
||||
0xA0..=0xAF => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
}
|
||||
|
||||
// Jump Instructions (0xB0-0xBF)
|
||||
0xB0..=0xBF => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
}
|
||||
|
||||
// Jump Instructions (0xC0-0xCF)
|
||||
0xC0..=0xCF => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
}
|
||||
|
||||
// Jump Instructions (0xD0-0xDF)
|
||||
0xD0..=0xDF => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
}
|
||||
|
||||
// Jump Instructions (0xE0-0xEF)
|
||||
0xE0..=0xEF => {
|
||||
let reg = opcode & 0x0F;
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
Instruction::Src(reg) // SRC R (continued)
|
||||
}
|
||||
|
||||
// Jump Instructions (0xF0-0xFF)
|
||||
0xF0..=0xFF => {
|
||||
match opcode {
|
||||
0xF0 => Instruction::Clb, // CLB
|
||||
0xF1 => Instruction::Clc, // CLC
|
||||
0xF2 => Instruction::Iac, // IAC
|
||||
0xF3 => Instruction::Cmc, // CMC
|
||||
0xF4 => Instruction::Cma, // CMA
|
||||
0xF5 => Instruction::Ral, // RAL
|
||||
0xF6 => Instruction::Rar, // RAR
|
||||
0xF7 => Instruction::Rar, // RAR (duplicate)
|
||||
0xF8 => Instruction::Daa, // DAA
|
||||
0xF9 => Instruction::Daa, // DAA (duplicate)
|
||||
0xFA => Instruction::Stc, // STC
|
||||
0xFB => Instruction::Stc, // STC (duplicate)
|
||||
0xFC => Instruction::Tcc, // TCC
|
||||
0xFD => Instruction::Tcs, // TCS
|
||||
0xF0 => Instruction::Clb, // CLB
|
||||
0xF1 => Instruction::Clc, // CLC
|
||||
0xF2 => Instruction::Iac, // IAC
|
||||
0xF3 => Instruction::Cmc, // CMC
|
||||
0xF4 => Instruction::Cma, // CMA
|
||||
0xF5 => Instruction::Ral, // RAL
|
||||
0xF6 => Instruction::Rar, // RAR
|
||||
0xF7 => Instruction::Rar, // RAR (duplicate)
|
||||
0xF8 => Instruction::Daa, // DAA
|
||||
0xF9 => Instruction::Daa, // DAA (duplicate)
|
||||
0xFA => Instruction::Stc, // STC
|
||||
0xFB => Instruction::Stc, // STC (duplicate)
|
||||
0xFC => Instruction::Tcc, // TCC
|
||||
0xFD => Instruction::Tcs, // TCS
|
||||
0xFE => Instruction::Invalid,
|
||||
0xFF => Instruction::Invalid,
|
||||
_ => Instruction::Invalid,
|
||||
|
|
@ -839,7 +839,9 @@ impl Intel4004 {
|
|||
|
||||
Instruction::Sub(reg) => {
|
||||
if reg < 16 {
|
||||
let result = self.accumulator.wrapping_sub(self.index_registers[reg as usize]);
|
||||
let result = self
|
||||
.accumulator
|
||||
.wrapping_sub(self.index_registers[reg as usize]);
|
||||
self.carry = self.accumulator < self.index_registers[reg as usize];
|
||||
self.accumulator = result & 0x0F;
|
||||
}
|
||||
|
|
@ -860,8 +862,12 @@ impl Intel4004 {
|
|||
Instruction::SubC(reg) => {
|
||||
if reg < 16 {
|
||||
let carry_val = if self.carry { 1 } else { 0 };
|
||||
let result = self.accumulator.wrapping_sub(self.index_registers[reg as usize]).wrapping_sub(carry_val);
|
||||
self.carry = self.accumulator < (self.index_registers[reg as usize] + carry_val);
|
||||
let result = self
|
||||
.accumulator
|
||||
.wrapping_sub(self.index_registers[reg as usize])
|
||||
.wrapping_sub(carry_val);
|
||||
self.carry =
|
||||
self.accumulator < (self.index_registers[reg as usize] + carry_val);
|
||||
self.accumulator = result & 0x0F;
|
||||
}
|
||||
self.program_counter.inc();
|
||||
|
|
@ -870,14 +876,16 @@ impl Intel4004 {
|
|||
// Logic Instructions
|
||||
Instruction::Ral => {
|
||||
let new_carry = (self.accumulator & 0x08) != 0;
|
||||
self.accumulator = ((self.accumulator << 1) | (if self.carry { 1 } else { 0 })) & 0x0F;
|
||||
self.accumulator =
|
||||
((self.accumulator << 1) | (if self.carry { 1 } else { 0 })) & 0x0F;
|
||||
self.carry = new_carry;
|
||||
self.program_counter.inc();
|
||||
}
|
||||
|
||||
Instruction::Rar => {
|
||||
let new_carry = (self.accumulator & 0x01) != 0;
|
||||
self.accumulator = ((self.accumulator >> 1) | (if self.carry { 0x08 } else { 0 })) & 0x0F;
|
||||
self.accumulator =
|
||||
((self.accumulator >> 1) | (if self.carry { 0x08 } else { 0 })) & 0x0F;
|
||||
self.carry = new_carry;
|
||||
self.program_counter.inc();
|
||||
}
|
||||
|
|
@ -947,22 +955,22 @@ impl Intel4004 {
|
|||
|
||||
Instruction::Jcn(condition, addr) => {
|
||||
let should_jump = match condition {
|
||||
0x0 => !self.carry, // JNT (Jump if no carry)
|
||||
0x1 => self.carry, // JC (Jump if carry)
|
||||
0x2 => self.accumulator == 0, // JZ (Jump if zero)
|
||||
0x3 => self.accumulator != 0, // JNZ (Jump if not zero)
|
||||
0x4 => true, // JUN (Jump unconditional)
|
||||
0x5 => false, // Always false
|
||||
0x6 => true, // Always true
|
||||
0x7 => false, // Always false
|
||||
0x8 => true, // Always true
|
||||
0x9 => false, // Always false
|
||||
0xA => true, // Always true
|
||||
0xB => false, // Always false
|
||||
0xC => true, // Always true
|
||||
0xD => false, // Always false
|
||||
0xE => true, // Always true
|
||||
0xF => false, // Always false
|
||||
0x0 => !self.carry, // JNT (Jump if no carry)
|
||||
0x1 => self.carry, // JC (Jump if carry)
|
||||
0x2 => self.accumulator == 0, // JZ (Jump if zero)
|
||||
0x3 => self.accumulator != 0, // JNZ (Jump if not zero)
|
||||
0x4 => true, // JUN (Jump unconditional)
|
||||
0x5 => false, // Always false
|
||||
0x6 => true, // Always true
|
||||
0x7 => false, // Always false
|
||||
0x8 => true, // Always true
|
||||
0x9 => false, // Always false
|
||||
0xA => true, // Always true
|
||||
0xB => false, // Always false
|
||||
0xC => true, // Always true
|
||||
0xD => false, // Always false
|
||||
0xE => true, // Always true
|
||||
0xF => false, // Always false
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
|
@ -1042,7 +1050,8 @@ impl Intel4004 {
|
|||
// Increment Register Instructions
|
||||
Instruction::Inc(reg) => {
|
||||
if reg < 16 {
|
||||
self.index_registers[reg as usize] = (self.index_registers[reg as usize] + 1) & 0x0F;
|
||||
self.index_registers[reg as usize] =
|
||||
(self.index_registers[reg as usize] + 1) & 0x0F;
|
||||
}
|
||||
self.program_counter.inc();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ use std::time::{Duration, Instant};
|
|||
|
||||
use crate::component::{BaseComponent, Component, RunnableComponent};
|
||||
use crate::components::common::intel_400x::{
|
||||
Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins,
|
||||
Intel400xDataBus, Intel400xResetHandling, Intel400xTimingState,
|
||||
MemoryState, TimingState
|
||||
Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins, Intel400xDataBus,
|
||||
Intel400xResetHandling, Intel400xTimingState, MemoryState, TimingState,
|
||||
};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
|
|
@ -49,7 +48,6 @@ enum IoMode {
|
|||
Bidirectional, // I/O pins bidirectional (not used in 4001)
|
||||
}
|
||||
|
||||
|
||||
impl Intel400xClockHandling for Intel4001 {
|
||||
fn get_base(&self) -> &BaseComponent {
|
||||
&self.base
|
||||
|
|
@ -396,8 +394,12 @@ impl Intel4001 {
|
|||
/// Hardware-accurate: Φ2 is when peripherals drive data, so we handle data driving
|
||||
/// Focus: Data driving operations
|
||||
fn handle_memory_data_operations(&mut self) {
|
||||
println!("DEBUG: {} - handle_memory_data_operations: state={:?}, address_ready={}",
|
||||
self.base.name(), self.memory_state, self.full_address_ready);
|
||||
println!(
|
||||
"DEBUG: {} - handle_memory_data_operations: state={:?}, address_ready={}",
|
||||
self.base.name(),
|
||||
self.memory_state,
|
||||
self.full_address_ready
|
||||
);
|
||||
match self.memory_state {
|
||||
MemoryState::Idle => {
|
||||
// During data phase, idle state means tri-state the bus
|
||||
|
|
@ -408,28 +410,43 @@ impl Intel4001 {
|
|||
MemoryState::AddressPhase => {
|
||||
// Address phase should be handled by Φ1, not Φ2
|
||||
// Tri-state bus during wrong phase
|
||||
println!("DEBUG: {} - In AddressPhase during Φ2, tri-stating", self.base.name());
|
||||
println!(
|
||||
"DEBUG: {} - In AddressPhase during Φ2, tri-stating",
|
||||
self.base.name()
|
||||
);
|
||||
self.tri_state_data_bus();
|
||||
}
|
||||
|
||||
MemoryState::WaitLatency => {
|
||||
// Address latched, waiting for access latency
|
||||
// Check if latency has elapsed and we can transition to data phase
|
||||
println!("DEBUG: {} - In WaitLatency, checking latency", self.base.name());
|
||||
println!(
|
||||
"DEBUG: {} - In WaitLatency, checking latency",
|
||||
self.base.name()
|
||||
);
|
||||
self.handle_latency_wait();
|
||||
// If we transitioned to DriveData, handle data driving
|
||||
if self.memory_state == MemoryState::DriveData {
|
||||
println!("DEBUG: {} - Transitioned to DriveData, calling handle_data_driving", self.base.name());
|
||||
println!(
|
||||
"DEBUG: {} - Transitioned to DriveData, calling handle_data_driving",
|
||||
self.base.name()
|
||||
);
|
||||
self.handle_data_driving();
|
||||
} else {
|
||||
println!("DEBUG: {} - Still in WaitLatency after handle_latency_wait", self.base.name());
|
||||
println!(
|
||||
"DEBUG: {} - Still in WaitLatency after handle_latency_wait",
|
||||
self.base.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryState::DriveData => {
|
||||
// Latency elapsed, drive data on bus during Φ2
|
||||
// Data will remain on bus until Φ2 falling edge
|
||||
println!("DEBUG: {} - In DriveData state, calling handle_data_driving", self.base.name());
|
||||
println!(
|
||||
"DEBUG: {} - In DriveData state, calling handle_data_driving",
|
||||
self.base.name()
|
||||
);
|
||||
self.handle_data_driving();
|
||||
}
|
||||
}
|
||||
|
|
@ -474,7 +491,9 @@ impl Intel4001 {
|
|||
} else if self.address_low_nibble.is_none() {
|
||||
// Second cycle: latch low nibble (bits 3-0) and transition to latency wait
|
||||
self.address_low_nibble = Some(nibble);
|
||||
if let Some(address) = self.assemble_full_address(self.address_high_nibble, self.address_low_nibble) {
|
||||
if let Some(address) =
|
||||
self.assemble_full_address(self.address_high_nibble, self.address_low_nibble)
|
||||
{
|
||||
self.last_address = address;
|
||||
self.start_latency_wait();
|
||||
}
|
||||
|
|
@ -493,16 +512,27 @@ impl Intel4001 {
|
|||
fn handle_latency_wait(&mut self) {
|
||||
if let Some(latch_time) = self.address_latch_time {
|
||||
let elapsed = latch_time.elapsed();
|
||||
println!("DEBUG: {} - handle_latency_wait: elapsed={:?}, access_time={:?}, ready={}",
|
||||
self.base.get_name(), elapsed, self.access_time, self.full_address_ready);
|
||||
println!(
|
||||
"DEBUG: {} - handle_latency_wait: elapsed={:?}, access_time={:?}, ready={}",
|
||||
self.base.get_name(),
|
||||
elapsed,
|
||||
self.access_time,
|
||||
self.full_address_ready
|
||||
);
|
||||
if elapsed >= self.access_time {
|
||||
// Latency elapsed, transition to data driving
|
||||
// Data will be driven on next Φ2 rising edge
|
||||
println!("DEBUG: {} - Latency elapsed, transitioning to DriveData", self.base.get_name());
|
||||
println!(
|
||||
"DEBUG: {} - Latency elapsed, transitioning to DriveData",
|
||||
self.base.get_name()
|
||||
);
|
||||
self.start_data_driving();
|
||||
}
|
||||
} else {
|
||||
println!("DEBUG: {} - handle_latency_wait: no latch_time set", self.base.get_name());
|
||||
println!(
|
||||
"DEBUG: {} - handle_latency_wait: no latch_time set",
|
||||
self.base.get_name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -520,8 +550,14 @@ impl Intel4001 {
|
|||
let chip_select = self.read_cm_rom_pin();
|
||||
let io_select = self.read_reset_pin(); // Using reset pin as CI for now
|
||||
|
||||
println!("DEBUG: {} - handle_data_driving: SYNC={}, CM={}, CI={}, Address_Ready={}",
|
||||
self.base.name(), sync, chip_select, io_select, self.full_address_ready);
|
||||
println!(
|
||||
"DEBUG: {} - handle_data_driving: SYNC={}, CM={}, CI={}, Address_Ready={}",
|
||||
self.base.name(),
|
||||
sync,
|
||||
chip_select,
|
||||
io_select,
|
||||
self.full_address_ready
|
||||
);
|
||||
|
||||
// Memory read: CM=1 (chip_select), CI=0 (!io_select), valid address
|
||||
if sync && chip_select && !io_select && self.full_address_ready {
|
||||
|
|
@ -530,12 +566,21 @@ impl Intel4001 {
|
|||
let address = self.last_address;
|
||||
if (address as usize) < self.memory.len() {
|
||||
let data = self.memory[address as usize];
|
||||
println!("DEBUG: {} - All conditions met, driving data 0x{:x} to address 0x{:x}", self.base.name(), data, address);
|
||||
println!(
|
||||
"DEBUG: {} - All conditions met, driving data 0x{:x} to address 0x{:x}",
|
||||
self.base.name(),
|
||||
data,
|
||||
address
|
||||
);
|
||||
self.write_data_bus(data);
|
||||
// Note: Don't call return_to_idle() here - wait for Φ2 falling edge
|
||||
} else {
|
||||
// Invalid address, tri-state
|
||||
println!("DEBUG: {} - Invalid address 0x{:x}, tri-stating", self.base.name(), address);
|
||||
println!(
|
||||
"DEBUG: {} - Invalid address 0x{:x}, tri-stating",
|
||||
self.base.name(),
|
||||
address
|
||||
);
|
||||
self.tri_state_data_bus();
|
||||
}
|
||||
} else {
|
||||
|
|
@ -682,9 +727,16 @@ impl Intel4001 {
|
|||
/// Debug function to log state transitions for troubleshooting
|
||||
/// Parameters: test_name - Name of the test for context
|
||||
pub fn debug_state_transitions(&self, test_name: &str) {
|
||||
println!("{} - State: {:?}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}, Latch time: {:?}",
|
||||
test_name, self.memory_state, self.address_high_nibble, self.address_low_nibble,
|
||||
self.last_address, self.full_address_ready, self.address_latch_time);
|
||||
println!(
|
||||
"{} - State: {:?}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}, Latch time: {:?}",
|
||||
test_name,
|
||||
self.memory_state,
|
||||
self.address_high_nibble,
|
||||
self.address_low_nibble,
|
||||
self.last_address,
|
||||
self.full_address_ready,
|
||||
self.address_latch_time
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -886,7 +938,7 @@ mod tests {
|
|||
assert_eq!(rom.memory_state, MemoryState::WaitLatency);
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1024,7 +1076,7 @@ mod tests {
|
|||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
rom.debug_state_transitions("BEFORE_SLEEP");
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
rom.debug_state_transitions("AFTER_SLEEP");
|
||||
|
||||
// --- Data phase ---
|
||||
|
|
@ -1049,14 +1101,20 @@ mod tests {
|
|||
// Debug: Check what data the ROM thinks it should drive
|
||||
let address = rom.last_address;
|
||||
let expected_data = rom.memory[address as usize];
|
||||
println!("DEBUG: ROM address 0x{:x}, data at address: 0x{:x}", address, expected_data);
|
||||
println!(
|
||||
"DEBUG: ROM address 0x{:x}, data at address: 0x{:x}",
|
||||
address, expected_data
|
||||
);
|
||||
|
||||
// Debug: Check the bit extraction logic
|
||||
let bit0 = (expected_data >> 0) & 1;
|
||||
let bit1 = (expected_data >> 1) & 1;
|
||||
let bit2 = (expected_data >> 2) & 1;
|
||||
let bit3 = (expected_data >> 3) & 1;
|
||||
println!("DEBUG: Bit extraction - bit0: {}, bit1: {}, bit2: {}, bit3: {}", bit0, bit1, bit2, bit3);
|
||||
println!(
|
||||
"DEBUG: Bit extraction - bit0: {}, bit1: {}, bit2: {}, bit3: {}",
|
||||
bit0, bit1, bit2, bit3
|
||||
);
|
||||
|
||||
// Debug: Check actual pin states and settlement status
|
||||
let d0_actual = d0_pin.lock().unwrap().read();
|
||||
|
|
@ -1074,10 +1132,22 @@ mod tests {
|
|||
let d2_settled = d2_pin.lock().unwrap().is_settled();
|
||||
let d3_settled = d3_pin.lock().unwrap().is_settled();
|
||||
|
||||
println!("DEBUG: D0 actual: {:?} (bit 0: {}), immediate: {:?}, settled: {}", d0_actual, bit0, d0_immediate, d0_settled);
|
||||
println!("DEBUG: D1 actual: {:?} (bit 1: {}), immediate: {:?}, settled: {}", d1_actual, bit1, d1_immediate, d1_settled);
|
||||
println!("DEBUG: D2 actual: {:?} (bit 2: {}), immediate: {:?}, settled: {}", d2_actual, bit2, d2_immediate, d2_settled);
|
||||
println!("DEBUG: D3 actual: {:?} (bit 3: {}), immediate: {:?}, settled: {}", d3_actual, bit3, d3_immediate, d3_settled);
|
||||
println!(
|
||||
"DEBUG: D0 actual: {:?} (bit 0: {}), immediate: {:?}, settled: {}",
|
||||
d0_actual, bit0, d0_immediate, d0_settled
|
||||
);
|
||||
println!(
|
||||
"DEBUG: D1 actual: {:?} (bit 1: {}), immediate: {:?}, settled: {}",
|
||||
d1_actual, bit1, d1_immediate, d1_settled
|
||||
);
|
||||
println!(
|
||||
"DEBUG: D2 actual: {:?} (bit 2: {}), immediate: {:?}, settled: {}",
|
||||
d2_actual, bit2, d2_immediate, d2_settled
|
||||
);
|
||||
println!(
|
||||
"DEBUG: D3 actual: {:?} (bit 3: {}), immediate: {:?}, settled: {}",
|
||||
d3_actual, bit3, d3_immediate, d3_settled
|
||||
);
|
||||
|
||||
// Debug: Check actual pin states immediately after data driving
|
||||
let d0_actual = d0_pin.lock().unwrap().read();
|
||||
|
|
@ -1085,7 +1155,10 @@ mod tests {
|
|||
let d2_actual = d2_pin.lock().unwrap().read();
|
||||
let d3_actual = d3_pin.lock().unwrap().read();
|
||||
|
||||
println!("DEBUG: After data driving - D0: {:?}, D1: {:?}, D2: {:?}, D3: {:?}", d0_actual, d1_actual, d2_actual, d3_actual);
|
||||
println!(
|
||||
"DEBUG: After data driving - D0: {:?}, D1: {:?}, D2: {:?}, D3: {:?}",
|
||||
d0_actual, d1_actual, d2_actual, d3_actual
|
||||
);
|
||||
|
||||
// Debug: Check what the ROM thinks it should be driving
|
||||
let expected_data = 0x12;
|
||||
|
|
@ -1093,14 +1166,17 @@ mod tests {
|
|||
let bit1 = (expected_data >> 1) & 1;
|
||||
let bit2 = (expected_data >> 2) & 1;
|
||||
let bit3 = (expected_data >> 3) & 1;
|
||||
println!("DEBUG: Expected bits - bit0: {}, bit1: {}, bit2: {}, bit3: {}", bit0, bit1, bit2, bit3);
|
||||
println!(
|
||||
"DEBUG: Expected bits - bit0: {}, bit1: {}, bit2: {}, bit3: {}",
|
||||
bit0, bit1, bit2, bit3
|
||||
);
|
||||
|
||||
// Verify data is driven on bus while Φ2 is high: 0x12 = 0001 0010
|
||||
// 0x12 = 18 decimal = 0001 0010 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=0
|
||||
|
||||
// Small delay to allow pin state to settle
|
||||
std::thread::sleep(Duration::from_micros(1));
|
||||
thread::sleep(Duration::from_micros(1));
|
||||
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1
|
||||
|
|
@ -1198,7 +1274,7 @@ mod tests {
|
|||
rom.update(); // falling edge
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// --- Data phase ---
|
||||
phi2_pin
|
||||
|
|
@ -1307,7 +1383,7 @@ mod tests {
|
|||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
|
|
@ -1383,7 +1459,7 @@ mod tests {
|
|||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
|
|
@ -1419,10 +1495,6 @@ mod tests {
|
|||
let sync_pin = rom.get_pin("SYNC").unwrap();
|
||||
let cm_pin = rom.get_pin("CM").unwrap();
|
||||
let ci_pin = rom.get_pin("CI").unwrap();
|
||||
let d0_pin = rom.get_pin("D0").unwrap();
|
||||
let d1_pin = rom.get_pin("D1").unwrap();
|
||||
let d2_pin = rom.get_pin("D2").unwrap();
|
||||
let d3_pin = rom.get_pin("D3").unwrap();
|
||||
|
||||
// Set up memory read operation: SYNC=1, chip_select=1, io_select=0 (memory access)
|
||||
{
|
||||
|
|
@ -1740,7 +1812,7 @@ mod tests {
|
|||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
|
|
@ -1815,7 +1887,7 @@ mod tests {
|
|||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
std::thread::sleep(Duration::from_nanos(2));
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ use std::thread;
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::component::{BaseComponent, Component, RunnableComponent};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
use crate::components::common::intel_400x::{
|
||||
Intel400xClockHandling, Intel400xDataBus, Intel400xAddressHandling,
|
||||
Intel400xControlPins, Intel400xResetHandling, Intel400xTimingState,
|
||||
TimingState, MemoryState, RamState, utils
|
||||
Intel400xAddressHandling, Intel400xClockHandling, Intel400xControlPins,
|
||||
Intel400xDataBus, Intel400xResetHandling, Intel400xTimingState, RamState,
|
||||
TimingState,
|
||||
};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
/// Intel 4002 - 320-bit RAM (80 nibbles × 4 bits) with integrated output ports
|
||||
/// Part of the MCS-4 family, designed to work with Intel 4004 CPU
|
||||
|
|
@ -28,39 +28,38 @@ use crate::components::common::intel_400x::{
|
|||
/// - Bank selection and status character handling follows Intel MCS-4 architecture
|
||||
pub struct Intel4002 {
|
||||
base: BaseComponent,
|
||||
variant: RamVariant, // RAM variant (4002-1 or 4002-2)
|
||||
memory: [u8; 80], // 80 nibbles of RAM (320 bits total) - 4 banks × 20 nibbles
|
||||
last_address: u8, // Last accessed memory address
|
||||
access_time: Duration, // RAM access latency (500ns typical)
|
||||
variant: RamVariant, // RAM variant (4002-1 or 4002-2)
|
||||
memory: [u8; 80], // 80 nibbles of RAM (320 bits total) - 4 banks × 20 nibbles
|
||||
last_address: u8, // Last accessed memory address
|
||||
access_time: Duration, // RAM access latency (500ns typical)
|
||||
address_latch_time: Option<Instant>, // Timestamp when address was latched
|
||||
output_ports: [u8; 4], // 4 output ports (4 bits each) - TODO: Make [[u8; 4]; 4] for 4-bit ports
|
||||
input_latch: u8, // Input data latch for I/O operations
|
||||
status_characters: [u8; 4], // 4 separate status character latches (4 bits each)
|
||||
bank_select: u8, // RAM bank selection (2 bits)
|
||||
output_ports: [u8; 4], // 4 output ports (4 bits each) - TODO: Make [[u8; 4]; 4] for 4-bit ports
|
||||
input_latch: u8, // Input data latch for I/O operations
|
||||
status_characters: [u8; 4], // 4 separate status character latches (4 bits each)
|
||||
bank_select: u8, // RAM bank selection (2 bits)
|
||||
// Clock edge detection (same as 4001)
|
||||
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
|
||||
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
|
||||
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
|
||||
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
|
||||
// Two-phase addressing for 8-bit address (same as 4001)
|
||||
address_high_nibble: Option<u8>, // High nibble of 8-bit address
|
||||
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
|
||||
full_address_ready: bool, // Whether complete address is assembled
|
||||
// RAM operation state machine
|
||||
ram_state: RamState, // Current state of RAM operation
|
||||
ram_state: RamState, // Current state of RAM operation
|
||||
// Data latching for RAM operations
|
||||
data_latch: Option<u8>, // Latched data for write operations
|
||||
data_latch: Option<u8>, // Latched data for write operations
|
||||
// Instruction cycle tracking
|
||||
instruction_phase: bool, // Whether we're in instruction phase
|
||||
current_instruction: u8, // Current instruction being processed
|
||||
instruction_phase: bool, // Whether we're in instruction phase
|
||||
current_instruction: u8, // Current instruction being processed
|
||||
}
|
||||
|
||||
|
||||
/// Intel 4002 RAM variants
|
||||
/// - 4002-1: Standard RAM variant
|
||||
/// - 4002-2: RAM variant with different configuration
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RamVariant {
|
||||
Type1, // 4002-1
|
||||
Type2, // 4002-2
|
||||
Type1, // 4002-1
|
||||
Type2, // 4002-2
|
||||
}
|
||||
|
||||
/// Intel 4002 timing constants (based on datasheet specifications)
|
||||
|
|
@ -68,10 +67,10 @@ pub enum RamVariant {
|
|||
struct TimingConstants;
|
||||
|
||||
impl TimingConstants {
|
||||
const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // T_ADDRESS_SETUP
|
||||
const DATA_VALID: Duration = Duration::from_nanos(200); // T_DATA_VALID
|
||||
const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // T_ADDRESS_SETUP
|
||||
const DATA_VALID: Duration = Duration::from_nanos(200); // T_DATA_VALID
|
||||
const OUTPUT_DISABLE: Duration = Duration::from_nanos(150); // T_OUTPUT_DISABLE
|
||||
const RAM_ACCESS: Duration = Duration::from_nanos(500); // RAM access time
|
||||
const RAM_ACCESS: Duration = Duration::from_nanos(500); // RAM access time
|
||||
}
|
||||
|
||||
impl Intel400xClockHandling for Intel4002 {
|
||||
|
|
@ -163,7 +162,7 @@ impl Intel4002 {
|
|||
Self::new_with_variant_and_access_time(name, RamVariant::Type1, 500) // Default 500ns access time
|
||||
}
|
||||
|
||||
/// Create a new Intel 4002 RAM with custom access time (for testing)
|
||||
/// Create a new Intel 4002 RAM with custom access time (for testing)
|
||||
/// Parameters: name - Component identifier, access_time_ns - Memory access time in nanoseconds
|
||||
/// Returns: New Intel4002 instance with configurable access timing
|
||||
pub fn new_with_access_time(name: String, access_time_ns: u64) -> Self {
|
||||
|
|
@ -173,7 +172,11 @@ impl Intel4002 {
|
|||
/// Create a new Intel 4002 RAM with specified variant and access time
|
||||
/// Parameters: name - Component identifier, variant - RAM variant (Type1 or Type2), access_time_ns - Memory access time in nanoseconds
|
||||
/// Returns: New Intel4002 instance with configurable variant and access timing
|
||||
pub fn new_with_variant_and_access_time(name: String, variant: RamVariant, access_time_ns: u64) -> Self {
|
||||
pub fn new_with_variant_and_access_time(
|
||||
name: String,
|
||||
variant: RamVariant,
|
||||
access_time_ns: u64,
|
||||
) -> Self {
|
||||
// Intel 4002 pinout (based on MCS-4 architecture):
|
||||
// - 4 data pins (D0-D3) for multiplexed address/data
|
||||
// - 4 output port pins (O0-O3)
|
||||
|
|
@ -186,28 +189,28 @@ impl Intel4002 {
|
|||
// - P0: RAM chip select (must be HIGH for RAM access)
|
||||
// - RESET: Clears internal state
|
||||
let pin_names = vec![
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"O0", "O1", "O2", "O3", // Output port pins
|
||||
"SYNC", // Sync signal
|
||||
"CM", // ROM Chip Select
|
||||
"P0", // RAM Chip Select
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
"SYNC", // Sync signal
|
||||
"CM", // ROM Chip Select
|
||||
"P0", // RAM Chip Select
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
];
|
||||
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
||||
Intel4002 {
|
||||
Intel4002 {
|
||||
base: BaseComponent::new(name, pins),
|
||||
variant,
|
||||
memory: [0u8; 80], // 80 nibbles = 4 banks × 20 nibbles each
|
||||
memory: [0u8; 80], // 80 nibbles = 4 banks × 20 nibbles each
|
||||
last_address: 0,
|
||||
access_time: Duration::from_nanos(access_time_ns),
|
||||
address_latch_time: None,
|
||||
output_ports: [0u8; 4],
|
||||
input_latch: 0,
|
||||
status_characters: [0u8; 4], // 4 separate status character latches
|
||||
status_characters: [0u8; 4], // 4 separate status character latches
|
||||
bank_select: 0,
|
||||
prev_phi1: PinValue::Low,
|
||||
prev_phi2: PinValue::Low,
|
||||
|
|
@ -312,7 +315,8 @@ impl Intel4002 {
|
|||
for i in 0..4 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
|
||||
pin_guard
|
||||
.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -324,13 +328,15 @@ impl Intel4002 {
|
|||
for port in 0..4 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("O{}", port)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.name())), PinValue::HighZ);
|
||||
pin_guard.set_driver(
|
||||
Some(format!("{}_OUTPUT", self.base.name())),
|
||||
PinValue::HighZ,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Read the two-phase clock pins from CPU
|
||||
/// Returns: (PHI1_value, PHI2_value)
|
||||
/// Hardware: Intel 4004 provides two-phase clock for synchronization
|
||||
|
|
@ -358,11 +364,6 @@ impl Intel4002 {
|
|||
(phi1, phi2)
|
||||
}
|
||||
|
||||
fn is_phi1_rising_edge(&self) -> bool {
|
||||
let (phi1, _) = self.read_clock_pins();
|
||||
phi1 == PinValue::High && self.prev_phi1 == PinValue::Low
|
||||
}
|
||||
|
||||
fn is_phi1_falling_edge(&self) -> bool {
|
||||
let (phi1, _) = self.read_clock_pins();
|
||||
phi1 == PinValue::Low && self.prev_phi1 == PinValue::High
|
||||
|
|
@ -436,10 +437,10 @@ impl Intel4002 {
|
|||
fn handle_reset(&mut self) {
|
||||
if Intel400xResetHandling::handle_reset(self, "RESET") {
|
||||
// Hardware reset - clear all registers specific to Intel4002
|
||||
self.memory = [0u8; 80]; // Clear 80 nibbles
|
||||
self.memory = [0u8; 80]; // Clear 80 nibbles
|
||||
self.output_ports = [0u8; 4];
|
||||
self.input_latch = 0;
|
||||
self.status_characters = [0u8; 4]; // Clear 4 status character latches
|
||||
self.status_characters = [0u8; 4]; // Clear 4 status character latches
|
||||
self.bank_select = 0;
|
||||
|
||||
// Reset all state machines using common functionality
|
||||
|
|
@ -464,7 +465,7 @@ impl Intel4002 {
|
|||
fn assemble_full_address(&mut self) {
|
||||
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
|
||||
// Assemble 8-bit address: (high << 4) | low
|
||||
self.last_address = ((high as u8) << 4) | (low as u8);
|
||||
self.last_address = (high << 4) | low;
|
||||
self.full_address_ready = true;
|
||||
self.address_latch_time = Some(Instant::now());
|
||||
|
||||
|
|
@ -482,18 +483,18 @@ impl Intel4002 {
|
|||
// The 4002 uses the high nibble of the address for bank selection
|
||||
// and the low nibble for within-bank addressing
|
||||
// Each bank has 20 nibbles (0-19), not 16
|
||||
let bank = (address_low >> 4) & 0x03; // 2-bit bank from high nibble
|
||||
let ram_address = (bank * 20) + (address_low & 0x0F); // 20 nibbles per bank
|
||||
let bank = (address_low >> 4) & 0x03; // 2-bit bank from high nibble
|
||||
let ram_address = (bank * 20) + (address_low & 0x0F); // 20 nibbles per bank
|
||||
|
||||
// Status characters are separate latches, not part of RAM
|
||||
if self.is_status_character_instruction(instruction) {
|
||||
// Status characters are separate from RAM - return special address
|
||||
return (bank, 0xFF); // Special marker for status character access
|
||||
return (bank, 0xFF); // Special marker for status character access
|
||||
}
|
||||
|
||||
// Output ports are also separate from RAM (addresses 0x14-0x17)
|
||||
if address_low >= 0x14 && address_low <= 0x17 {
|
||||
return (bank, 0xFF); // Special marker for output port access
|
||||
return (bank, 0xFF); // Special marker for output port access
|
||||
}
|
||||
|
||||
(bank, ram_address)
|
||||
|
|
@ -833,9 +834,9 @@ impl Intel4002 {
|
|||
|
||||
/// Handle instruction cycle synchronization
|
||||
fn handle_instruction_cycle(&mut self) {
|
||||
let (sync, _cm, p0, _) = self.read_control_pins();
|
||||
let (sync, _cm, _p0, _) = self.read_control_pins();
|
||||
|
||||
if sync && self.is_phi1_rising_edge() {
|
||||
if sync && self.is_phi1_rising_edge(self.prev_phi1) {
|
||||
// Start of new instruction cycle
|
||||
self.instruction_phase = true;
|
||||
self.current_instruction = self.read_data_bus();
|
||||
|
|
@ -1071,7 +1072,7 @@ impl Intel4002 {
|
|||
|
||||
/// Clear all RAM to zero
|
||||
pub fn clear_ram(&mut self) {
|
||||
self.memory = [0u8; 80]; // Clear 80 nibbles
|
||||
self.memory = [0u8; 80]; // Clear 80 nibbles
|
||||
}
|
||||
|
||||
/// Get all RAM data for a specific bank
|
||||
|
|
@ -1079,7 +1080,7 @@ impl Intel4002 {
|
|||
/// Returns: Vector of 20 nibbles for the bank (4 banks × 20 nibbles = 80 total)
|
||||
pub fn get_ram_bank(&self, bank: u8) -> Vec<u8> {
|
||||
let bank = (bank & 0x03) as usize;
|
||||
let start = bank * 20; // 20 nibbles per bank
|
||||
let start = bank * 20; // 20 nibbles per bank
|
||||
let end = start + 20;
|
||||
|
||||
if end <= self.memory.len() {
|
||||
|
|
@ -1094,9 +1095,16 @@ impl Intel4002 {
|
|||
/// Debug function to log state transitions for troubleshooting
|
||||
/// Parameters: test_name - Name of the test for context
|
||||
pub fn debug_state_transitions(&self, test_name: &str) {
|
||||
println!("{} - State: {:?}, Bank: {}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}",
|
||||
test_name, self.ram_state, self.bank_select, self.address_high_nibble,
|
||||
self.address_low_nibble, self.last_address, self.full_address_ready);
|
||||
println!(
|
||||
"{} - State: {:?}, Bank: {}, High: {:?}, Low: {:?}, Address: 0x{:x}, Ready: {}",
|
||||
test_name,
|
||||
self.ram_state,
|
||||
self.bank_select,
|
||||
self.address_high_nibble,
|
||||
self.address_low_nibble,
|
||||
self.last_address,
|
||||
self.full_address_ready
|
||||
);
|
||||
}
|
||||
|
||||
/// Setup helper for instruction testing
|
||||
|
|
@ -1104,12 +1112,6 @@ impl Intel4002 {
|
|||
self.current_instruction = instruction;
|
||||
self.instruction_phase = true;
|
||||
}
|
||||
|
||||
/// Tri-state all outputs (comprehensive tri-state)
|
||||
fn tri_state_all_outputs(&self) {
|
||||
self.tri_state_data_bus();
|
||||
self.tri_state_output_ports();
|
||||
}
|
||||
}
|
||||
|
||||
// Custom formatter for debugging
|
||||
|
|
@ -1182,7 +1184,7 @@ mod tests {
|
|||
let mut ram = Intel4002::new("RAM_4002".to_string());
|
||||
|
||||
// Write test data to different banks
|
||||
ram.write_ram(0, 0x01).unwrap(); // Bank 0, address 0
|
||||
ram.write_ram(0, 0x01).unwrap(); // Bank 0, address 0
|
||||
ram.write_ram(20, 0x02).unwrap(); // Bank 1, address 0
|
||||
ram.write_ram(40, 0x03).unwrap(); // Bank 2, address 0
|
||||
|
||||
|
|
@ -1272,9 +1274,15 @@ mod tests {
|
|||
}
|
||||
|
||||
// High nibble
|
||||
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::High);
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
ram.update(); // rising edge -> latch high nibble
|
||||
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::Low);
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
ram.update(); // falling edge
|
||||
|
||||
// Should have transitioned to AddressPhase
|
||||
|
|
@ -1294,9 +1302,15 @@ mod tests {
|
|||
}
|
||||
|
||||
// Low nibble
|
||||
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::High);
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
ram.update(); // rising edge -> latch low nibble
|
||||
phi1_pin.lock().unwrap().set_driver(Some("TEST".into()), PinValue::Low);
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
ram.update(); // falling edge
|
||||
|
||||
// Should have assembled full address and transitioned to WaitLatency
|
||||
|
|
@ -1446,13 +1460,13 @@ mod tests {
|
|||
assert_eq!(ram.read_ram(4).unwrap(), 0x05);
|
||||
|
||||
// Test overflow
|
||||
let large_data = vec![0u8; 81]; // More than 80 nibbles
|
||||
let large_data = vec![0u8; 81]; // More than 80 nibbles
|
||||
assert!(ram.initialize_ram(&large_data).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ram_state_transitions() {
|
||||
let mut ram = Intel4002::new_with_access_time("RAM_4002".to_string(), 1);
|
||||
let ram = Intel4002::new_with_access_time("RAM_4002".to_string(), 1);
|
||||
|
||||
// Initially should be in idle state
|
||||
assert_eq!(ram.ram_state, RamState::Idle);
|
||||
|
|
@ -1500,8 +1514,8 @@ mod tests {
|
|||
|
||||
// SRC instruction sets up output port addressing
|
||||
ram.setup_instruction_test(0x25); // SRC with specific address
|
||||
// Verify output port addressing is set up correctly
|
||||
// This would require more complex setup in a real implementation
|
||||
// Verify output port addressing is set up correctly
|
||||
// This would require more complex setup in a real implementation
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ use crate::pin::{Pin, PinValue};
|
|||
/// - Output behavior matches 4001/4002 I/O persistence model
|
||||
pub struct Intel4003 {
|
||||
base: BaseComponent,
|
||||
shift_register: [u8; 10], // 10-bit shift register (stored as 10 bytes for simplicity)
|
||||
output_latch: [u8; 10], // 10-bit output latch for parallel output
|
||||
serial_input: u8, // Serial input data (4-bit)
|
||||
access_time: Duration, // Shift register access latency (200ns typical)
|
||||
shift_register: [u8; 10], // 10-bit shift register (stored as 10 bytes for simplicity)
|
||||
output_latch: [u8; 10], // 10-bit output latch for parallel output
|
||||
serial_input: u8, // Serial input data (4-bit)
|
||||
access_time: Duration, // Shift register access latency (200ns typical)
|
||||
|
||||
// Clock edge detection
|
||||
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
|
||||
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
|
||||
prev_phi1: PinValue, // Previous Φ1 clock state for edge detection
|
||||
prev_phi2: PinValue, // Previous Φ2 clock state for edge detection
|
||||
|
||||
// Two-phase addressing for 8-bit address
|
||||
address_high_nibble: Option<u8>, // High nibble of 8-bit address
|
||||
|
|
@ -36,7 +36,7 @@ pub struct Intel4003 {
|
|||
full_address_ready: bool, // Whether complete address is assembled
|
||||
|
||||
// Shift register operation state machine
|
||||
shift_state: ShiftState, // Current state of shift operation
|
||||
shift_state: ShiftState, // Current state of shift operation
|
||||
address_latch_time: Option<Instant>, // Timestamp when address was latched
|
||||
}
|
||||
|
||||
|
|
@ -74,21 +74,21 @@ impl Intel4003 {
|
|||
// - CM: Chip select (must be HIGH for shift register access)
|
||||
// - RESET: Clears internal state
|
||||
let pin_names = vec![
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins
|
||||
"SYNC", // Sync signal
|
||||
"CM", // Chip Select
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", // 10 output pins
|
||||
"SYNC", // Sync signal
|
||||
"CM", // Chip Select
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
];
|
||||
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
||||
Intel4003 {
|
||||
base: BaseComponent::new(name, pins),
|
||||
shift_register: [0u8; 10], // 10-bit shift register
|
||||
output_latch: [0u8; 10], // 10-bit output latch
|
||||
shift_register: [0u8; 10], // 10-bit shift register
|
||||
output_latch: [0u8; 10], // 10-bit output latch
|
||||
serial_input: 0,
|
||||
access_time: Duration::from_nanos(access_time_ns),
|
||||
|
||||
|
|
@ -167,7 +167,8 @@ impl Intel4003 {
|
|||
for i in 0..4 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
|
||||
pin_guard
|
||||
.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +179,10 @@ impl Intel4003 {
|
|||
for i in 0..10 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("O{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
pin_guard.set_driver(Some(format!("{}_OUTPUT", self.base.name())), PinValue::HighZ);
|
||||
pin_guard.set_driver(
|
||||
Some(format!("{}_OUTPUT", self.base.name())),
|
||||
PinValue::HighZ,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -274,7 +278,7 @@ impl Intel4003 {
|
|||
|
||||
/// Assemble complete 8-bit address from high and low nibbles
|
||||
fn assemble_full_address(&mut self) {
|
||||
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
|
||||
if let (Some(_high), Some(_low)) = (self.address_high_nibble, self.address_low_nibble) {
|
||||
// Assemble 8-bit address: (high << 4) | low
|
||||
self.full_address_ready = true;
|
||||
self.address_latch_time = Some(Instant::now());
|
||||
|
|
@ -400,29 +404,41 @@ impl Intel4003 {
|
|||
if sync && chip_select && self.full_address_ready {
|
||||
// Read serial input data from data bus
|
||||
let serial_data = self.read_data_bus();
|
||||
println!("DEBUG: 4003 shift operation - serial_data = 0x{:x}", serial_data);
|
||||
println!(
|
||||
"DEBUG: 4003 shift operation - serial_data = 0x{:x}",
|
||||
serial_data
|
||||
);
|
||||
|
||||
// Shift in the new bits (serial input)
|
||||
// For 4003, we shift in 4 bits at a time
|
||||
// First, shift existing bits right by 4 positions
|
||||
for j in (4..10).rev() {
|
||||
self.shift_register[j] = self.shift_register[j-4];
|
||||
self.shift_register[j] = self.shift_register[j - 4];
|
||||
}
|
||||
|
||||
// Then insert the 4 new bits into positions 0-3
|
||||
for i in 0..4 {
|
||||
let bit = (serial_data >> i) & 1;
|
||||
self.shift_register[i] = bit;
|
||||
println!("DEBUG: 4003 shift step {} - inserted bit {} at position {}", i, bit, i);
|
||||
println!(
|
||||
"DEBUG: 4003 shift step {} - inserted bit {} at position {}",
|
||||
i, bit, i
|
||||
);
|
||||
}
|
||||
|
||||
// Update output latch with new shift register contents
|
||||
self.output_latch.copy_from_slice(&self.shift_register);
|
||||
self.update_output_pins();
|
||||
|
||||
println!("DEBUG: 4003 shift register after operation: {:?}", self.shift_register);
|
||||
println!(
|
||||
"DEBUG: 4003 shift register after operation: {:?}",
|
||||
self.shift_register
|
||||
);
|
||||
let (high, low) = self.get_shift_register();
|
||||
println!("DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}", high, low);
|
||||
println!(
|
||||
"DEBUG: 4003 get_shift_register() returns high=0x{:x}, low=0x{:x}",
|
||||
high, low
|
||||
);
|
||||
|
||||
self.shift_state = ShiftState::OutputData;
|
||||
} else {
|
||||
|
|
@ -436,7 +452,6 @@ impl Intel4003 {
|
|||
// This state maintains the output until next operation
|
||||
}
|
||||
|
||||
|
||||
/// Get the current shift register value
|
||||
/// Returns: 10-bit value as a tuple (high_byte, low_2_bits)
|
||||
pub fn get_shift_register(&self) -> (u8, u8) {
|
||||
|
|
@ -593,9 +608,9 @@ mod tests {
|
|||
let mut d2_guard = d2_pin.lock().unwrap();
|
||||
let mut d3_guard = d3_pin.lock().unwrap();
|
||||
d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 0 = 1
|
||||
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0
|
||||
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0
|
||||
d2_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Bit 2 = 1
|
||||
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0
|
||||
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0
|
||||
}
|
||||
|
||||
// Set control pins for shift operation
|
||||
|
|
@ -613,7 +628,10 @@ mod tests {
|
|||
|
||||
// Verify shift operation occurred
|
||||
let (high, low) = sr.get_shift_register();
|
||||
println!("DEBUG: After shift operation - high=0x{:x}, low=0x{:x}", high, low);
|
||||
println!(
|
||||
"DEBUG: After shift operation - high=0x{:x}, low=0x{:x}",
|
||||
high, low
|
||||
);
|
||||
println!("DEBUG: Shift register contents: {:?}", sr.shift_register);
|
||||
|
||||
// The shift register should now contain the shifted data
|
||||
|
|
@ -623,7 +641,7 @@ mod tests {
|
|||
// high byte: 01010101 (0x55) - this is correct, the first 4 bits are 0101 (0x05)
|
||||
// low byte: 00000001 (0x01) - this is the remaining 2 bits from the original pattern
|
||||
assert_eq!(high, 0x55); // First 8 bits should be 01010101
|
||||
assert_eq!(low, 0x01); // Lower 2 bits should be 01
|
||||
assert_eq!(low, 0x01); // Lower 2 bits should be 01
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -670,4 +688,4 @@ mod tests {
|
|||
assert_eq!(fast_sr.get_access_time(), 1);
|
||||
assert_eq!(fast_sr.name(), "FAST_SHIFT");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue