Introduced comments to some files

Improved accuracy and test of the Intel 4001 ROM.
Tests currently fail on the 4001.
This commit is contained in:
Jan-Peter von Hunnius 2025-09-27 21:37:54 +02:00
parent cff459d2b5
commit 5bc1916b2f
3 changed files with 1420 additions and 172 deletions

View file

@ -8,17 +8,45 @@ use std::time::Duration;
use crate::pin::Pin;
// Component is now inherently thread-safe
/// Core trait for all hardware components in the emulator
/// Provides the fundamental interface that all components must implement
/// Components are inherently thread-safe and can be used across thread boundaries
pub trait Component: Send + Sync {
/// Get the name/identifier of this component
/// Returns: Component name as a String
fn name(&self) -> String;
/// Get all pins associated with this component
/// Returns: HashMap mapping pin names to pin objects
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>>;
/// Get a specific pin by name
/// Parameters: name - The name of the pin to retrieve
/// Returns: Ok(pin) if found, Err(String) if pin not found
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String>;
/// Update the component state for one simulation cycle
/// This method is called repeatedly during component execution
fn update(&mut self);
/// Run the component in a continuous loop until stopped
/// This method blocks until the component is stopped
fn run(&mut self);
/// Stop the component and clean up resources
/// This method should tri-state all outputs and prepare for shutdown
fn stop(&mut self);
/// Check if the component is currently running
/// Returns: true if component is running, false otherwise
fn is_running(&self) -> bool;
}
/// Extended trait for components that can be run in their own threads
/// Provides automatic thread spawning functionality for components
pub trait RunnableComponent: Component + Send + 'static {
/// Spawn the component in its own thread
/// Parameters: self - The component to spawn (consumed by the thread)
/// Returns: JoinHandle for the spawned thread
fn spawn_in_thread(mut self) -> thread::JoinHandle<()>
where
Self: Sized,
@ -28,7 +56,9 @@ pub trait RunnableComponent: Component + Send + 'static {
})
}
}
// BaseComponent uses AtomicBool for thread-safe state
/// Base implementation of the Component trait providing common functionality
/// Handles thread-safe state management and basic pin operations
/// Most hardware components should embed this struct to inherit common behavior
pub struct BaseComponent {
name: String,
pins: HashMap<String, Arc<Mutex<Pin>>>,
@ -36,6 +66,9 @@ pub struct BaseComponent {
}
impl BaseComponent {
/// Create a new BaseComponent with the specified name and pins
/// Parameters: name - Component identifier, pins - Pin mapping for the component
/// Returns: New BaseComponent instance
pub fn new(name: String, pins: HashMap<String, Arc<Mutex<Pin>>>) -> Self {
BaseComponent {
name,
@ -44,18 +77,27 @@ impl BaseComponent {
}
}
/// Get the name of this component
/// Returns: Component name as string slice
pub fn get_name(&self) -> &str {
&self.name
}
/// Check if the component is currently running
/// Returns: true if running, false otherwise
pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
/// Set the running state of the component
/// Parameters: running - New running state
pub fn set_running(&self, running: bool) {
self.running.store(running, Ordering::SeqCst);
}
/// Create a pin mapping from a list of pin names
/// Parameters: pin_names - List of pin name strings, component_name - Name of the component
/// Returns: HashMap mapping pin names to Pin objects with proper naming
pub fn create_pin_map(
pin_names: &[&str],
component_name: &str,
@ -90,10 +132,14 @@ impl Component for BaseComponent {
.ok_or_else(|| format!("Pin {} not found", name))
}
/// Update the component state for one simulation cycle
/// Base implementation does nothing - should be overridden by specific components
fn update(&mut self) {
// Base implementation does nothing
}
/// Run the component in a continuous loop until stopped
/// Provides a default time-sliced execution model with 10 microsecond delays
fn run(&mut self) {
self.set_running(true);
while self.is_running() {
@ -102,6 +148,8 @@ impl Component for BaseComponent {
}
}
/// Stop the component and clean up resources
/// Sets running state to false to exit the run loop
fn stop(&mut self) {
self.set_running(false);
}

View file

@ -7,41 +7,51 @@ use crate::component::{BaseComponent, Component, RunnableComponent};
use crate::pin::{Pin, PinValue};
use crate::types::U12;
/// Represents the current phase of instruction execution
/// The 4004 CPU processes instructions in distinct phases synchronized with the clock
#[derive(Debug, Clone, Copy, PartialEq)]
enum InstructionPhase {
Fetch,
Address,
Execute,
Wait,
Fetch, // Fetching instruction from memory
Address, // Calculating or fetching address
Execute, // Executing the instruction
Wait, // Waiting for external operations
}
/// Represents the current phase of the two-phase clock cycle
/// The 4004 uses a two-phase clock for synchronization with peripherals
#[derive(Debug, Clone, Copy, PartialEq)]
enum ClockPhase {
Phase1,
Phase2,
Phase1, // First clock phase - CPU drives bus
Phase2, // Second clock phase - Peripherals drive bus
}
/// Intel 4004 4-bit microprocessor implementation
/// The world's first microprocessor, featuring 4-bit data bus, 12-bit addressing,
/// 46 instructions, and 16 index registers. Part of the MCS-4 family.
pub struct Intel4004 {
base: BaseComponent,
accumulator: u8,
carry: bool,
index_registers: [u8; 16],
pub(crate) program_counter: U12,
stack: [U12; 3],
stack_pointer: u8,
cycle_count: u64,
instruction_phase: InstructionPhase,
current_instruction: u8,
address_latch: u8,
data_latch: u8,
clock_speed: f64,
last_clock_transition: Instant,
clock_phase: ClockPhase,
rom_port: u8,
ram_bank: u8,
accumulator: u8, // Main accumulator register (4-bit)
carry: bool, // Carry flag for arithmetic operations
index_registers: [u8; 16], // 16 4-bit index registers (R0-R15)
pub(crate) program_counter: U12, // 12-bit program counter
stack: [U12; 3], // 3-level 12-bit address stack
stack_pointer: u8, // Stack pointer (0-2)
cycle_count: u64, // Total number of clock cycles executed
instruction_phase: InstructionPhase, // Current instruction execution phase
current_instruction: u8, // Currently executing instruction
address_latch: u8, // Latched address for memory operations
data_latch: u8, // Latched data for memory operations
clock_speed: f64, // Target clock speed in Hz
last_clock_transition: Instant, // Timestamp of last clock transition
clock_phase: ClockPhase, // Current clock phase
rom_port: u8, // Currently selected ROM port (0-15)
ram_bank: u8, // Currently selected RAM bank (0-7)
}
impl Intel4004 {
/// Create a new Intel 4004 CPU instance
/// Parameters: name - Component identifier, clock_speed - Target clock frequency in Hz
/// Returns: New Intel4004 instance with initialized state
pub fn new(name: String, clock_speed: f64) -> Self {
let pin_names = vec![
"D0", "D1", "D2", "D3", "SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2",
@ -72,11 +82,16 @@ impl Intel4004 {
}
}
/// Set the initial program counter value for the CPU
/// Parameters: self - CPU instance, pc - Initial 12-bit program counter value
/// Returns: Modified CPU instance with new program counter
pub fn with_initial_pc(mut self, pc: u16) -> Self {
self.program_counter = U12::new(pc);
self
}
/// Reset the CPU to its initial state
/// Clears all registers, resets program counter, and tri-states all outputs
pub fn reset(&mut self) {
self.accumulator = 0;
self.carry = false;
@ -94,6 +109,8 @@ impl Intel4004 {
self.tri_state_data_bus();
}
/// Read the 4-bit data bus from D0-D3 pins
/// Returns: 4-bit value from data bus pins
fn read_data_bus(&self) -> u8 {
let mut data = 0;
@ -110,6 +127,8 @@ impl Intel4004 {
data & 0x0F
}
/// Drive the 4-bit data bus with the specified value
/// Parameters: data - 4-bit value to drive on D0-D3 pins
fn write_data_bus(&self, data: u8) {
let nibble = data & 0x0F;
@ -128,6 +147,8 @@ impl Intel4004 {
}
}
/// Set data bus to high-impedance state to avoid bus contention
/// CRITICAL: Must be called whenever CPU is not actively driving valid data
fn tri_state_data_bus(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
@ -139,6 +160,8 @@ impl Intel4004 {
}
}
/// Set the SYNC pin state
/// Parameters: high - true for high voltage, false for low voltage
fn set_sync(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(mut pin_guard) = pin.lock() {
@ -148,6 +171,8 @@ impl Intel4004 {
}
}
/// Set the CM-ROM (Chip Select ROM) pin state
/// Parameters: high - true for high voltage, false for low voltage
fn set_cm_rom(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM_ROM") {
if let Ok(mut pin_guard) = pin.lock() {
@ -157,6 +182,8 @@ impl Intel4004 {
}
}
/// Set the CM-RAM (Chip Select RAM) pin state
/// Parameters: high - true for high voltage, false for low voltage
fn set_cm_ram(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM_RAM") {
if let Ok(mut pin_guard) = pin.lock() {
@ -166,6 +193,8 @@ impl Intel4004 {
}
}
/// Read the state of all control pins
/// Returns: (sync, cm_rom, cm_ram, test) - State of control signals
fn read_control_pins(&self) -> (bool, bool, bool, bool) {
let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() {
@ -181,12 +210,15 @@ impl Intel4004 {
(sync, false, false, true)
}
/// Handle clock cycle processing
/// Updates cycle count and manages clock phase transitions
fn handle_clock(&mut self) {
// Simple clock simulation for now
self.cycle_count += 1;
}
// Simplified instruction execution for Fibonacci demo
/// Execute a single instruction (simplified for demo purposes)
/// For Fibonacci demo, simulate simple execution with periodic accumulator updates
fn execute_instruction(&mut self) {
// For Fibonacci demo, simulate simple execution
// Just increment PC and occasionally update accumulator to simulate Fibonacci sequence
@ -200,6 +232,9 @@ impl Intel4004 {
}
}
/// Simulate Fibonacci number calculation for demo purposes
/// Parameters: n - Fibonacci sequence index
/// Returns: nth Fibonacci number modulo 16 (4-bit value)
fn simulate_fibonacci(&self, n: u8) -> u8 {
match n {
0 => 0,
@ -217,39 +252,57 @@ impl Intel4004 {
}
}
// Public methods used by MCS-4 system
/// Get the current program counter value
/// Returns: 12-bit program counter as 16-bit value
pub fn get_program_counter(&self) -> u16 {
self.program_counter.value()
}
/// Set the program counter to a specific address
/// Parameters: address - New 12-bit program counter value
pub fn set_program_counter(&mut self, address: u16) {
self.program_counter.set(address);
}
/// Get the current accumulator value
/// Returns: 4-bit accumulator value
pub fn get_accumulator(&self) -> u8 {
self.accumulator
}
/// Set the accumulator to a specific value
/// Parameters: value - New 4-bit accumulator value (will be masked to 4 bits)
pub fn set_accumulator(&mut self, value: u8) {
self.accumulator = value & 0x0F;
}
/// Get the current carry flag state
/// Returns: true if carry is set, false otherwise
pub fn get_carry(&self) -> bool {
self.carry
}
/// Get the current stack pointer value
/// Returns: Stack pointer (0-2 for the 3-level stack)
pub fn get_stack_pointer(&self) -> u8 {
self.stack_pointer
}
/// Get the total number of clock cycles executed
/// Returns: Total cycle count since reset
pub fn get_cycle_count(&self) -> u64 {
self.cycle_count
}
/// Get the configured clock speed
/// Returns: Clock speed in Hz
pub fn get_clock_speed(&self) -> f64 {
self.clock_speed
}
/// Set an index register to a specific value
/// Parameters: index - Register index (0-15), value - New 4-bit register value
/// Returns: Ok(()) if successful, Err(String) if index out of range
pub fn set_register(&mut self, index: u8, value: u8) -> Result<(), String> {
if index < 16 {
self.index_registers[index as usize] = value & 0x0F;
@ -259,6 +312,9 @@ impl Intel4004 {
}
}
/// Get the value of an index register
/// Parameters: index - Register index (0-15)
/// Returns: Some(register_value) if index valid, None if out of range
pub fn get_register(&self, index: u8) -> Option<u8> {
if index < 16 {
Some(self.index_registers[index as usize])
@ -281,6 +337,8 @@ impl Component for Intel4004 {
self.base.get_pin(name)
}
/// Update the CPU state for one simulation cycle
/// Processes clock cycles and executes instructions when running
fn update(&mut self) {
if !self.is_running() {
return;
@ -290,6 +348,8 @@ impl Component for Intel4004 {
self.execute_instruction();
}
/// Run the CPU in a continuous loop until stopped
/// Provides a time-sliced execution model with 10 microsecond delays between cycles
fn run(&mut self) {
// Time-slice model: run in a loop calling update() each cycle
self.base.set_running(true);
@ -301,6 +361,8 @@ impl Component for Intel4004 {
}
}
/// Stop the CPU and clean up resources
/// Tri-states all outputs and prepares for shutdown
fn stop(&mut self) {
self.base.set_running(false);
self.tri_state_data_bus();
@ -316,4 +378,5 @@ impl Component for Intel4004 {
impl RunnableComponent for Intel4004 {
// No custom run_loop needed - uses default Component::run() method
// The default implementation spawns the component in its own thread
}

File diff suppressed because it is too large Load diff