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; 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 { pub trait Component: Send + Sync {
/// Get the name/identifier of this component
/// Returns: Component name as a String
fn name(&self) -> 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>>>; 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>; 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); 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); 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); fn stop(&mut self);
/// Check if the component is currently running
/// Returns: true if component is running, false otherwise
fn is_running(&self) -> bool; 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 { 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<()> fn spawn_in_thread(mut self) -> thread::JoinHandle<()>
where where
Self: Sized, 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 { pub struct BaseComponent {
name: String, name: String,
pins: HashMap<String, Arc<Mutex<Pin>>>, pins: HashMap<String, Arc<Mutex<Pin>>>,
@ -36,6 +66,9 @@ pub struct BaseComponent {
} }
impl 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 { pub fn new(name: String, pins: HashMap<String, Arc<Mutex<Pin>>>) -> Self {
BaseComponent { BaseComponent {
name, 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 { pub fn get_name(&self) -> &str {
&self.name &self.name
} }
/// Check if the component is currently running
/// Returns: true if running, false otherwise
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst) self.running.load(Ordering::SeqCst)
} }
/// Set the running state of the component
/// Parameters: running - New running state
pub fn set_running(&self, running: bool) { pub fn set_running(&self, running: bool) {
self.running.store(running, Ordering::SeqCst); 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( pub fn create_pin_map(
pin_names: &[&str], pin_names: &[&str],
component_name: &str, component_name: &str,
@ -90,10 +132,14 @@ impl Component for BaseComponent {
.ok_or_else(|| format!("Pin {} not found", name)) .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) { fn update(&mut self) {
// Base implementation does nothing // 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) { fn run(&mut self) {
self.set_running(true); self.set_running(true);
while self.is_running() { 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) { fn stop(&mut self) {
self.set_running(false); self.set_running(false);
} }

View file

@ -7,41 +7,51 @@ use crate::component::{BaseComponent, Component, RunnableComponent};
use crate::pin::{Pin, PinValue}; use crate::pin::{Pin, PinValue};
use crate::types::U12; 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)] #[derive(Debug, Clone, Copy, PartialEq)]
enum InstructionPhase { enum InstructionPhase {
Fetch, Fetch, // Fetching instruction from memory
Address, Address, // Calculating or fetching address
Execute, Execute, // Executing the instruction
Wait, 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)] #[derive(Debug, Clone, Copy, PartialEq)]
enum ClockPhase { enum ClockPhase {
Phase1, Phase1, // First clock phase - CPU drives bus
Phase2, 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 { pub struct Intel4004 {
base: BaseComponent, base: BaseComponent,
accumulator: u8, accumulator: u8, // Main accumulator register (4-bit)
carry: bool, carry: bool, // Carry flag for arithmetic operations
index_registers: [u8; 16], index_registers: [u8; 16], // 16 4-bit index registers (R0-R15)
pub(crate) program_counter: U12, pub(crate) program_counter: U12, // 12-bit program counter
stack: [U12; 3], stack: [U12; 3], // 3-level 12-bit address stack
stack_pointer: u8, stack_pointer: u8, // Stack pointer (0-2)
cycle_count: u64, cycle_count: u64, // Total number of clock cycles executed
instruction_phase: InstructionPhase, instruction_phase: InstructionPhase, // Current instruction execution phase
current_instruction: u8, current_instruction: u8, // Currently executing instruction
address_latch: u8, address_latch: u8, // Latched address for memory operations
data_latch: u8, data_latch: u8, // Latched data for memory operations
clock_speed: f64, clock_speed: f64, // Target clock speed in Hz
last_clock_transition: Instant, last_clock_transition: Instant, // Timestamp of last clock transition
clock_phase: ClockPhase, clock_phase: ClockPhase, // Current clock phase
rom_port: u8, rom_port: u8, // Currently selected ROM port (0-15)
ram_bank: u8, ram_bank: u8, // Currently selected RAM bank (0-7)
} }
impl Intel4004 { 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 { pub fn new(name: String, clock_speed: f64) -> Self {
let pin_names = vec![ let pin_names = vec![
"D0", "D1", "D2", "D3", "SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2", "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 { pub fn with_initial_pc(mut self, pc: u16) -> Self {
self.program_counter = U12::new(pc); self.program_counter = U12::new(pc);
self self
} }
/// Reset the CPU to its initial state
/// Clears all registers, resets program counter, and tri-states all outputs
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.accumulator = 0; self.accumulator = 0;
self.carry = false; self.carry = false;
@ -94,6 +109,8 @@ impl Intel4004 {
self.tri_state_data_bus(); 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 { fn read_data_bus(&self) -> u8 {
let mut data = 0; let mut data = 0;
@ -110,6 +127,8 @@ impl Intel4004 {
data & 0x0F 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) { fn write_data_bus(&self, data: u8) {
let nibble = data & 0x0F; 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) { fn tri_state_data_bus(&self) {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { 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) { fn set_sync(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("SYNC") { if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(mut pin_guard) = pin.lock() { 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) { fn set_cm_rom(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM_ROM") { if let Ok(pin) = self.base.get_pin("CM_ROM") {
if let Ok(mut pin_guard) = pin.lock() { 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) { fn set_cm_ram(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM_RAM") { if let Ok(pin) = self.base.get_pin("CM_RAM") {
if let Ok(mut pin_guard) = pin.lock() { 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) { fn read_control_pins(&self) -> (bool, bool, bool, bool) {
let sync = if let Ok(pin) = self.base.get_pin("SYNC") { let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() { if let Ok(pin_guard) = pin.lock() {
@ -181,12 +210,15 @@ impl Intel4004 {
(sync, false, false, true) (sync, false, false, true)
} }
/// Handle clock cycle processing
/// Updates cycle count and manages clock phase transitions
fn handle_clock(&mut self) { fn handle_clock(&mut self) {
// Simple clock simulation for now // Simple clock simulation for now
self.cycle_count += 1; 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) { fn execute_instruction(&mut self) {
// For Fibonacci demo, simulate simple execution // For Fibonacci demo, simulate simple execution
// Just increment PC and occasionally update accumulator to simulate Fibonacci sequence // 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 { fn simulate_fibonacci(&self, n: u8) -> u8 {
match n { match n {
0 => 0, 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 { pub fn get_program_counter(&self) -> u16 {
self.program_counter.value() 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) { pub fn set_program_counter(&mut self, address: u16) {
self.program_counter.set(address); self.program_counter.set(address);
} }
/// Get the current accumulator value
/// Returns: 4-bit accumulator value
pub fn get_accumulator(&self) -> u8 { pub fn get_accumulator(&self) -> u8 {
self.accumulator 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) { pub fn set_accumulator(&mut self, value: u8) {
self.accumulator = value & 0x0F; self.accumulator = value & 0x0F;
} }
/// Get the current carry flag state
/// Returns: true if carry is set, false otherwise
pub fn get_carry(&self) -> bool { pub fn get_carry(&self) -> bool {
self.carry 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 { pub fn get_stack_pointer(&self) -> u8 {
self.stack_pointer self.stack_pointer
} }
/// Get the total number of clock cycles executed
/// Returns: Total cycle count since reset
pub fn get_cycle_count(&self) -> u64 { pub fn get_cycle_count(&self) -> u64 {
self.cycle_count self.cycle_count
} }
/// Get the configured clock speed
/// Returns: Clock speed in Hz
pub fn get_clock_speed(&self) -> f64 { pub fn get_clock_speed(&self) -> f64 {
self.clock_speed 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> { pub fn set_register(&mut self, index: u8, value: u8) -> Result<(), String> {
if index < 16 { if index < 16 {
self.index_registers[index as usize] = value & 0x0F; 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> { pub fn get_register(&self, index: u8) -> Option<u8> {
if index < 16 { if index < 16 {
Some(self.index_registers[index as usize]) Some(self.index_registers[index as usize])
@ -281,6 +337,8 @@ impl Component for Intel4004 {
self.base.get_pin(name) 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) { fn update(&mut self) {
if !self.is_running() { if !self.is_running() {
return; return;
@ -290,6 +348,8 @@ impl Component for Intel4004 {
self.execute_instruction(); 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) { fn run(&mut self) {
// Time-slice model: run in a loop calling update() each cycle // Time-slice model: run in a loop calling update() each cycle
self.base.set_running(true); 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) { fn stop(&mut self) {
self.base.set_running(false); self.base.set_running(false);
self.tri_state_data_bus(); self.tri_state_data_bus();
@ -316,4 +378,5 @@ impl Component for Intel4004 {
impl RunnableComponent for Intel4004 { impl RunnableComponent for Intel4004 {
// No custom run_loop needed - uses default Component::run() method // 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