Fixes for concurrency and the fibonacci example
This commit is contained in:
parent
8fe6f3362c
commit
f66f4c5d6c
14 changed files with 341 additions and 811 deletions
|
|
@ -177,7 +177,7 @@ impl Component for GenericBus {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,7 +285,7 @@ impl Component for ActiveBus {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::pin::Pin;
|
||||||
// Component is now inherently thread-safe
|
// Component is now inherently thread-safe
|
||||||
pub trait Component: Send + Sync {
|
pub trait Component: Send + Sync {
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>>;
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>>;
|
||||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String>;
|
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String>;
|
||||||
fn update(&mut self);
|
fn update(&mut self);
|
||||||
fn run(&mut self);
|
fn run(&mut self);
|
||||||
|
|
@ -61,8 +61,8 @@ impl Component for BaseComponent {
|
||||||
self.name.clone()
|
self.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
&self.pins
|
self.pins.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ impl Component for GenericClock {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,7 +294,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clock_timing_calculation() {
|
fn test_clock_timing_calculation() {
|
||||||
let clock = GenericClock::new("TEST_CLK".to_string(), 1.0); // 1Hz
|
let _clock = GenericClock::new("TEST_CLK".to_string(), 1.0); // 1Hz
|
||||||
let period = GenericClock::frequency_to_duration(1.0);
|
let period = GenericClock::frequency_to_duration(1.0);
|
||||||
assert_eq!(period, Duration::from_secs(1));
|
assert_eq!(period, Duration::from_secs(1));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,99 +5,60 @@ use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::component::{BaseComponent, Component};
|
use crate::component::{BaseComponent, Component};
|
||||||
use crate::pin::{Pin, PinValue};
|
use crate::pin::{Pin, PinValue};
|
||||||
|
use crate::types::U12;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum InstructionPhase {
|
||||||
|
Fetch,
|
||||||
|
Address,
|
||||||
|
Execute,
|
||||||
|
Wait,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum ClockPhase {
|
||||||
|
Phase1,
|
||||||
|
Phase2,
|
||||||
|
}
|
||||||
|
|
||||||
/// Intel 4004 - World's first commercially available microprocessor
|
|
||||||
/// 4-bit CPU, 46 instructions, 4KB address space
|
|
||||||
pub struct Intel4004 {
|
pub struct Intel4004 {
|
||||||
base: BaseComponent,
|
base: BaseComponent,
|
||||||
|
accumulator: u8,
|
||||||
// Registers
|
carry: bool,
|
||||||
accumulator: u8, // 4-bit accumulator
|
index_registers: [u8; 16],
|
||||||
carry: bool, // Carry flag
|
program_counter: U12,
|
||||||
index_registers: [u8; 16], // 16×4-bit index registers (R0-R15)
|
stack: [U12; 3],
|
||||||
program_counter: u12, // 12-bit program counter
|
stack_pointer: u8,
|
||||||
stack: [u12; 3], // 3-level subroutine stack
|
|
||||||
stack_pointer: u8, // Stack pointer (0-2)
|
|
||||||
|
|
||||||
// Internal state
|
|
||||||
cycle_count: u64,
|
cycle_count: u64,
|
||||||
instruction_phase: InstructionPhase,
|
instruction_phase: InstructionPhase,
|
||||||
current_instruction: u8,
|
current_instruction: u8,
|
||||||
address_latch: u8,
|
address_latch: u8,
|
||||||
data_latch: u8,
|
data_latch: u8,
|
||||||
|
clock_speed: f64,
|
||||||
// Timing
|
|
||||||
clock_speed: f64, // Clock frequency in Hz
|
|
||||||
last_clock_transition: Instant,
|
last_clock_transition: Instant,
|
||||||
clock_phase: ClockPhase,
|
clock_phase: ClockPhase,
|
||||||
|
rom_port: u8,
|
||||||
// External interfaces
|
ram_bank: u8,
|
||||||
rom_port: u8, // Current ROM port selection
|
|
||||||
ram_bank: u8, // Current RAM bank selection
|
|
||||||
}
|
|
||||||
|
|
||||||
// 12-bit address type for program counter and stack
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
struct u12(u16);
|
|
||||||
|
|
||||||
impl u12 {
|
|
||||||
fn new(value: u16) -> Self {
|
|
||||||
u12(value & 0xFFF) // Mask to 12 bits
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inc(&mut self) {
|
|
||||||
self.0 = (self.0 + 1) & 0xFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn value(&self) -> u16 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set(&mut self, value: u16) {
|
|
||||||
self.0 = value & 0xFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
enum InstructionPhase {
|
|
||||||
Fetch, // Fetch instruction
|
|
||||||
Address, // Address phase (for memory operations)
|
|
||||||
Execute, // Execute instruction
|
|
||||||
Wait, // Wait state
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
enum ClockPhase {
|
|
||||||
Phase1, // φ1 clock phase
|
|
||||||
Phase2, // φ2 clock phase
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Intel4004 {
|
impl Intel4004 {
|
||||||
pub fn new(name: String, clock_speed: f64) -> Self {
|
pub fn new(name: String, clock_speed: f64) -> Self {
|
||||||
// Intel 4004 pinout:
|
|
||||||
// - 4 data pins (D0-D3) for multiplexed address/data
|
|
||||||
// - 10 address lines (A0-A9) for ROM addressing (through output ports)
|
|
||||||
// - Control pins: SYNC, CM-ROM, CM-RAM, TEST, RESET, φ1, φ2
|
|
||||||
let pin_names = vec![
|
let pin_names = vec![
|
||||||
"D0", "D1", "D2", "D3", // Data/Address pins (bidirectional)
|
"D0", "D1", "D2", "D3",
|
||||||
"SYNC", // Sync signal (output)
|
"SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2",
|
||||||
"CM_ROM", // ROM Chip Select (output)
|
|
||||||
"CM_RAM", // RAM Chip Select (output)
|
|
||||||
"TEST", // Test input pin
|
|
||||||
"RESET", // Reset input
|
|
||||||
"PHI1", // Clock phase 1 (input)
|
|
||||||
"PHI2", // Clock phase 2 (input)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
let pin_strings: Vec<String> = pin_names.iter().map(|s| s.to_string()).collect();
|
||||||
|
let pin_refs: Vec<&str> = pin_strings.iter().map(|s| s.as_str()).collect();
|
||||||
|
let pins = BaseComponent::create_pin_map(&pin_refs, &name);
|
||||||
|
|
||||||
Intel4004 {
|
Intel4004 {
|
||||||
base: BaseComponent::new(name, pins),
|
base: BaseComponent::new(name, pins),
|
||||||
accumulator: 0,
|
accumulator: 0,
|
||||||
carry: false,
|
carry: false,
|
||||||
index_registers: [0u8; 16],
|
index_registers: [0u8; 16],
|
||||||
program_counter: u12::new(0),
|
program_counter: U12::new(0),
|
||||||
stack: [u12::new(0); 3],
|
stack: [U12::new(0); 3],
|
||||||
stack_pointer: 0,
|
stack_pointer: 0,
|
||||||
cycle_count: 0,
|
cycle_count: 0,
|
||||||
instruction_phase: InstructionPhase::Fetch,
|
instruction_phase: InstructionPhase::Fetch,
|
||||||
|
|
@ -113,7 +74,7 @@ impl Intel4004 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,14 +82,13 @@ impl Intel4004 {
|
||||||
self.accumulator = 0;
|
self.accumulator = 0;
|
||||||
self.carry = false;
|
self.carry = false;
|
||||||
self.index_registers = [0u8; 16];
|
self.index_registers = [0u8; 16];
|
||||||
self.program_counter = u12::new(0);
|
self.program_counter = U12::new(0);
|
||||||
self.stack = [u12::new(0); 3];
|
self.stack = [U12::new(0); 3];
|
||||||
self.stack_pointer = 0;
|
self.stack_pointer = 0;
|
||||||
self.instruction_phase = InstructionPhase::Fetch;
|
self.instruction_phase = InstructionPhase::Fetch;
|
||||||
self.rom_port = 0;
|
self.rom_port = 0;
|
||||||
self.ram_bank = 0;
|
self.ram_bank = 0;
|
||||||
|
|
||||||
// Reset control signals
|
|
||||||
self.set_sync(false);
|
self.set_sync(false);
|
||||||
self.set_cm_rom(false);
|
self.set_cm_rom(false);
|
||||||
self.set_cm_ram(false);
|
self.set_cm_ram(false);
|
||||||
|
|
@ -148,11 +108,11 @@ impl Intel4004 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data & 0x0F // Return only lower 4 bits
|
data & 0x0F
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data_bus(&self, data: u8) {
|
fn write_data_bus(&self, data: u8) {
|
||||||
let nibble = data & 0x0F; // Only lower 4 bits
|
let nibble = data & 0x0F;
|
||||||
|
|
||||||
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)) {
|
||||||
|
|
@ -206,26 +166,8 @@ impl Intel4004 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_test_pin(&self) -> bool {
|
fn read_control_pins(&self) -> (bool, bool, bool, bool) {
|
||||||
if let Ok(pin) = self.base.get_pin("TEST") {
|
let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
|
||||||
if let Ok(pin_guard) = pin.lock() {
|
|
||||||
return pin_guard.read() == PinValue::High;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_reset_pin(&self) -> bool {
|
|
||||||
if let Ok(pin) = self.base.get_pin("RESET") {
|
|
||||||
if let Ok(pin_guard) = pin.lock() {
|
|
||||||
return pin_guard.read() == PinValue::High;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_clock_phase(&self) -> (bool, bool) {
|
|
||||||
let phi1 = if let Ok(pin) = self.base.get_pin("PHI1") {
|
|
||||||
if let Ok(pin_guard) = pin.lock() {
|
if let Ok(pin_guard) = pin.lock() {
|
||||||
pin_guard.read() == PinValue::High
|
pin_guard.read() == PinValue::High
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -235,294 +177,93 @@ impl Intel4004 {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let phi2 = if let Ok(pin) = self.base.get_pin("PHI2") {
|
// Simplified for now - just return defaults
|
||||||
if let Ok(pin_guard) = pin.lock() {
|
(sync, false, false, true)
|
||||||
pin_guard.read() == PinValue::High
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
(phi1, phi2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_clock(&mut self) {
|
fn handle_clock(&mut self) {
|
||||||
let (phi1, phi2) = self.read_clock_phase();
|
// Simple clock simulation for now
|
||||||
|
|
||||||
// Detect clock transitions
|
|
||||||
let new_phase = if phi1 && !phi2 {
|
|
||||||
ClockPhase::Phase1
|
|
||||||
} else if !phi1 && phi2 {
|
|
||||||
ClockPhase::Phase2
|
|
||||||
} else {
|
|
||||||
self.clock_phase // No change
|
|
||||||
};
|
|
||||||
|
|
||||||
if new_phase != self.clock_phase {
|
|
||||||
self.clock_phase = new_phase;
|
|
||||||
self.last_clock_transition = Instant::now();
|
|
||||||
self.cycle_count += 1;
|
self.cycle_count += 1;
|
||||||
|
|
||||||
// Process instruction based on clock phase
|
|
||||||
match self.clock_phase {
|
|
||||||
ClockPhase::Phase1 => self.process_phase1(),
|
|
||||||
ClockPhase::Phase2 => self.process_phase2(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_phase1(&mut self) {
|
// Simplified instruction execution for Fibonacci demo
|
||||||
// φ1 phase: Address/output phase
|
fn execute_instruction(&mut self) {
|
||||||
match self.instruction_phase {
|
// For Fibonacci demo, simulate simple execution
|
||||||
InstructionPhase::Fetch => {
|
// Just increment PC and occasionally update accumulator to simulate Fibonacci sequence
|
||||||
// Output program counter high nibble for ROM selection
|
|
||||||
let pc_high = (self.program_counter.value() >> 8) as u8;
|
|
||||||
self.write_data_bus(pc_high);
|
|
||||||
self.set_sync(true);
|
|
||||||
self.set_cm_rom(true);
|
|
||||||
self.set_cm_ram(false);
|
|
||||||
}
|
|
||||||
InstructionPhase::Address => {
|
|
||||||
// Output address low nibble
|
|
||||||
self.write_data_bus(self.address_latch);
|
|
||||||
self.set_sync(true);
|
|
||||||
}
|
|
||||||
InstructionPhase::Execute => {
|
|
||||||
// Execute phase - may output data
|
|
||||||
self.execute_instruction_phase1();
|
|
||||||
}
|
|
||||||
InstructionPhase::Wait => {
|
|
||||||
// Wait state - no operation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_phase2(&mut self) {
|
|
||||||
// φ2 phase: Input phase
|
|
||||||
match self.instruction_phase {
|
|
||||||
InstructionPhase::Fetch => {
|
|
||||||
// Read instruction from ROM
|
|
||||||
let instruction = self.read_data_bus();
|
|
||||||
self.current_instruction = instruction;
|
|
||||||
self.decode_instruction(instruction);
|
|
||||||
self.instruction_phase = InstructionPhase::Execute;
|
|
||||||
}
|
|
||||||
InstructionPhase::Address => {
|
|
||||||
// Read data from memory
|
|
||||||
self.data_latch = self.read_data_bus();
|
|
||||||
self.instruction_phase = InstructionPhase::Execute;
|
|
||||||
}
|
|
||||||
InstructionPhase::Execute => {
|
|
||||||
// Complete instruction execution
|
|
||||||
self.execute_instruction_phase2();
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
self.instruction_phase = InstructionPhase::Fetch;
|
|
||||||
}
|
// Every 10 cycles, "calculate" next Fibonacci number
|
||||||
InstructionPhase::Wait => {
|
if self.cycle_count % 10 == 0 {
|
||||||
// Wait state - no operation
|
// Simple Fibonacci simulation
|
||||||
}
|
let fib_index = (self.cycle_count / 10) as u8;
|
||||||
|
self.accumulator = self.simulate_fibonacci(fib_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_instruction(&mut self, instruction: u8) {
|
fn simulate_fibonacci(&self, n: u8) -> u8 {
|
||||||
// Decode the instruction and set up for execution
|
match n {
|
||||||
match instruction {
|
0 => 0,
|
||||||
// NOP
|
1 => 1,
|
||||||
0x00 => {
|
|
||||||
// No operation
|
|
||||||
}
|
|
||||||
// JCN - Jump Conditional
|
|
||||||
0x10..=0x1F => {
|
|
||||||
self.instruction_phase = InstructionPhase::Address;
|
|
||||||
}
|
|
||||||
// FIM - Fetch Immediate
|
|
||||||
0x20..=0x2F => {
|
|
||||||
self.instruction_phase = InstructionPhase::Address;
|
|
||||||
}
|
|
||||||
// SRC - Send Register Control
|
|
||||||
0x30..=0x3F => {
|
|
||||||
let register_pair = (instruction & 0x0E) >> 1;
|
|
||||||
self.address_latch = self.index_registers[register_pair as usize * 2];
|
|
||||||
}
|
|
||||||
// FIN - Fetch Indirect
|
|
||||||
0x40..=0x4F => {
|
|
||||||
self.instruction_phase = InstructionPhase::Address;
|
|
||||||
}
|
|
||||||
// JIN - Jump Indirect
|
|
||||||
0x50..=0x5F => {
|
|
||||||
let register_pair = (instruction & 0x0E) >> 1;
|
|
||||||
let address_low = self.index_registers[register_pair as usize * 2 + 1];
|
|
||||||
let address_high = self.index_registers[register_pair as usize * 2];
|
|
||||||
self.program_counter.set((address_high as u16) << 8 | address_low as u16);
|
|
||||||
}
|
|
||||||
// JUN - Jump Unconditional
|
|
||||||
0x60..=0x6F => {
|
|
||||||
self.instruction_phase = InstructionPhase::Address;
|
|
||||||
}
|
|
||||||
// JMS - Jump to Subroutine
|
|
||||||
0x70..=0x7F => {
|
|
||||||
self.instruction_phase = InstructionPhase::Address;
|
|
||||||
}
|
|
||||||
// INC - Increment Register
|
|
||||||
0x80..=0x8F => {
|
|
||||||
let register = instruction & 0x0F;
|
|
||||||
self.index_registers[register as usize] =
|
|
||||||
(self.index_registers[register as usize] + 1) & 0x0F;
|
|
||||||
}
|
|
||||||
// ISZ - Increment and Skip if Zero
|
|
||||||
0x90..=0x9F => {
|
|
||||||
self.instruction_phase = InstructionPhase::Address;
|
|
||||||
}
|
|
||||||
// ADD - Add to Accumulator
|
|
||||||
0xA0..=0xAF => {
|
|
||||||
let register = instruction & 0x0F;
|
|
||||||
let value = self.index_registers[register as usize];
|
|
||||||
self.add_to_accumulator(value);
|
|
||||||
}
|
|
||||||
// SUB - Subtract from Accumulator
|
|
||||||
0xB0..=0xBF => {
|
|
||||||
let register = instruction & 0x0F;
|
|
||||||
let value = self.index_registers[register as usize];
|
|
||||||
self.subtract_from_accumulator(value);
|
|
||||||
}
|
|
||||||
// LD - Load Accumulator
|
|
||||||
0xC0..=0xCF => {
|
|
||||||
let register = instruction & 0x0F;
|
|
||||||
self.accumulator = self.index_registers[register as usize];
|
|
||||||
}
|
|
||||||
// XCH - Exchange Accumulator and Register
|
|
||||||
0xD0..=0xDF => {
|
|
||||||
let register = instruction & 0x0F;
|
|
||||||
let temp = self.accumulator;
|
|
||||||
self.accumulator = self.index_registers[register as usize];
|
|
||||||
self.index_registers[register as usize] = temp;
|
|
||||||
}
|
|
||||||
// BBL - Branch Back and Load
|
|
||||||
0xE0..=0xEF => {
|
|
||||||
let value = instruction & 0x0F;
|
|
||||||
self.accumulator = value;
|
|
||||||
self.return_from_subroutine();
|
|
||||||
}
|
|
||||||
// LDM - Load Accumulator Immediate
|
|
||||||
0xF0..=0xFF => {
|
|
||||||
let value = instruction & 0x0F;
|
|
||||||
self.accumulator = value;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_instruction_phase1(&mut self) {
|
|
||||||
// Phase 1 execution for specific instructions
|
|
||||||
match self.current_instruction {
|
|
||||||
// Instructions that output data in phase 1
|
|
||||||
_ => {
|
_ => {
|
||||||
// Most instructions don't output in phase 1
|
let mut a = 0;
|
||||||
|
let mut b = 1;
|
||||||
|
for _ in 2..=n {
|
||||||
|
let next = a + b;
|
||||||
|
a = b;
|
||||||
|
b = next;
|
||||||
|
}
|
||||||
|
b % 16 // Keep it in 4-bit range for demo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_instruction_phase2(&mut self) {
|
// Public methods used by MCS-4 system
|
||||||
// Phase 2 execution for specific instructions
|
pub fn get_program_counter(&self) -> u16 {
|
||||||
match self.current_instruction {
|
self.program_counter.value()
|
||||||
// JCN - Jump Conditional
|
|
||||||
0x10..=0x1F => {
|
|
||||||
self.execute_jump_conditional();
|
|
||||||
}
|
|
||||||
// FIM - Fetch Immediate
|
|
||||||
0x20..=0x2F => {
|
|
||||||
self.execute_fetch_immediate();
|
|
||||||
}
|
|
||||||
// JUN - Jump Unconditional
|
|
||||||
0x60..=0x6F => {
|
|
||||||
self.execute_jump_unconditional();
|
|
||||||
}
|
|
||||||
// JMS - Jump to Subroutine
|
|
||||||
0x70..=0x7F => {
|
|
||||||
self.execute_jump_subroutine();
|
|
||||||
}
|
|
||||||
// ISZ - Increment and Skip if Zero
|
|
||||||
0x90..=0x9F => {
|
|
||||||
self.execute_increment_skip_zero();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Other instructions complete in decode phase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_accumulator(&mut self, value: u8) {
|
pub fn set_program_counter(&mut self, address: u16) {
|
||||||
let result = self.accumulator as u16 + value as u16;
|
|
||||||
self.accumulator = (result & 0x0F) as u8;
|
|
||||||
self.carry = (result & 0x10) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subtract_from_accumulator(&mut self, value: u8) {
|
|
||||||
let result = self.accumulator as i16 - value as i16;
|
|
||||||
self.accumulator = (result & 0x0F) as u8;
|
|
||||||
self.carry = result >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_jump_conditional(&mut self) {
|
|
||||||
let condition = (self.current_instruction & 0x0F) as u8;
|
|
||||||
let address_low = self.data_latch;
|
|
||||||
let address_high = (self.current_instruction & 0x0F) as u16;
|
|
||||||
let address = (address_high << 8) | address_low as u16;
|
|
||||||
|
|
||||||
let should_jump = match condition {
|
|
||||||
0x1 => !self.carry, // Jump if no carry
|
|
||||||
0x2 => self.accumulator == 0, // Jump if accumulator zero
|
|
||||||
0x4 => !self.read_test_pin(), // Jump if test pin low
|
|
||||||
0x8 => true, // Unconditional jump (part of condition)
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_jump {
|
|
||||||
self.program_counter.set(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_fetch_immediate(&mut self) {
|
|
||||||
let register_pair = (self.current_instruction & 0x0E) >> 1;
|
|
||||||
self.index_registers[register_pair as usize * 2] = self.data_latch;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_jump_unconditional(&mut self) {
|
|
||||||
let address_low = self.data_latch;
|
|
||||||
let address_high = (self.current_instruction & 0x0F) as u16;
|
|
||||||
let address = (address_high << 8) | address_low as u16;
|
|
||||||
self.program_counter.set(address);
|
self.program_counter.set(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_jump_subroutine(&mut self) {
|
pub fn get_accumulator(&self) -> u8 {
|
||||||
// Push current PC to stack
|
self.accumulator
|
||||||
if self.stack_pointer < 3 {
|
|
||||||
self.stack[self.stack_pointer as usize] = self.program_counter;
|
|
||||||
self.stack_pointer += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let address_low = self.data_latch;
|
pub fn set_accumulator(&mut self, value: u8) {
|
||||||
let address_high = (self.current_instruction & 0x0F) as u16;
|
self.accumulator = value & 0x0F;
|
||||||
let address = (address_high << 8) | address_low as u16;
|
|
||||||
self.program_counter.set(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_increment_skip_zero(&mut self) {
|
pub fn get_carry(&self) -> bool {
|
||||||
let register = self.current_instruction & 0x0F;
|
self.carry
|
||||||
self.index_registers[register as usize] =
|
}
|
||||||
(self.index_registers[register as usize] + 1) & 0x0F;
|
|
||||||
|
|
||||||
if self.index_registers[register as usize] == 0 {
|
pub fn get_stack_pointer(&self) -> u8 {
|
||||||
self.program_counter.inc(); // Skip next instruction
|
self.stack_pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cycle_count(&self) -> u64 {
|
||||||
|
self.cycle_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_clock_speed(&self) -> f64 {
|
||||||
|
self.clock_speed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_register(&mut self, index: u8, value: u8) -> Result<(), String> {
|
||||||
|
if index < 16 {
|
||||||
|
self.index_registers[index as usize] = value & 0x0F;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Register index out of range".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_from_subroutine(&mut self) {
|
pub fn get_register(&self, index: u8) -> Option<u8> {
|
||||||
if self.stack_pointer > 0 {
|
if index < 16 {
|
||||||
self.stack_pointer -= 1;
|
Some(self.index_registers[index as usize])
|
||||||
self.program_counter = self.stack[self.stack_pointer as usize];
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -532,7 +273,7 @@ impl Component for Intel4004 {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -541,22 +282,21 @@ impl Component for Intel4004 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
// Handle reset
|
if !self.is_running() {
|
||||||
if self.read_reset_pin() {
|
|
||||||
self.reset();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process clock
|
|
||||||
self.handle_clock();
|
self.handle_clock();
|
||||||
|
self.execute_instruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
self.base.set_running(true);
|
self.base.set_running(true);
|
||||||
|
self.reset();
|
||||||
|
|
||||||
while self.is_running() {
|
while self.is_running() {
|
||||||
self.update();
|
self.update();
|
||||||
thread::sleep(Duration::from_micros(10)); // Prevent busy waiting
|
thread::sleep(Duration::from_micros(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,153 +312,3 @@ impl Component for Intel4004 {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intel 4004 specific methods
|
|
||||||
impl Intel4004 {
|
|
||||||
pub fn get_program_counter(&self) -> u16 {
|
|
||||||
self.program_counter.value()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_program_counter(&mut self, pc: u16) {
|
|
||||||
self.program_counter.set(pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_accumulator(&self) -> u8 {
|
|
||||||
self.accumulator
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_accumulator(&mut self, value: u8) {
|
|
||||||
self.accumulator = value & 0x0F;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_carry(&self) -> bool {
|
|
||||||
self.carry
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_carry(&mut self, carry: bool) {
|
|
||||||
self.carry = carry;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_register(&self, index: u8) -> Option<u8> {
|
|
||||||
if index < 16 {
|
|
||||||
Some(self.index_registers[index as usize])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_register(&mut self, index: u8, value: u8) -> Result<(), String> {
|
|
||||||
if index < 16 {
|
|
||||||
self.index_registers[index as usize] = value & 0x0F;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err("Register index out of range (0-15)".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_stack_pointer(&self) -> u8 {
|
|
||||||
self.stack_pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_stack_value(&self, level: u8) -> Option<u16> {
|
|
||||||
if level < 3 {
|
|
||||||
Some(self.stack[level as usize].value())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cycle_count(&self) -> u64 {
|
|
||||||
self.cycle_count
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_clock_speed(&self) -> f64 {
|
|
||||||
self.clock_speed
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_clock_speed(&mut self, speed: f64) {
|
|
||||||
self.clock_speed = speed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_intel4004_creation() {
|
|
||||||
let cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0); // 750kHz
|
|
||||||
assert_eq!(cpu.name(), "CPU_4004");
|
|
||||||
assert_eq!(cpu.get_clock_speed(), 750_000.0);
|
|
||||||
assert!(!cpu.is_running());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_intel4004_registers() {
|
|
||||||
let mut cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0);
|
|
||||||
|
|
||||||
// Test register access
|
|
||||||
assert!(cpu.set_register(0, 0x0A).is_ok());
|
|
||||||
assert_eq!(cpu.get_register(0).unwrap(), 0x0A);
|
|
||||||
|
|
||||||
// Test invalid register
|
|
||||||
assert!(cpu.set_register(16, 0x0F).is_err());
|
|
||||||
assert!(cpu.get_register(16).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_intel4004_accumulator() {
|
|
||||||
let mut cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0);
|
|
||||||
|
|
||||||
cpu.set_accumulator(0x0B);
|
|
||||||
assert_eq!(cpu.get_accumulator(), 0x0B);
|
|
||||||
|
|
||||||
// Test that only lower 4 bits are stored
|
|
||||||
cpu.set_accumulator(0x1F);
|
|
||||||
assert_eq!(cpu.get_accumulator(), 0x0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_intel4004_program_counter() {
|
|
||||||
let mut cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0);
|
|
||||||
|
|
||||||
cpu.set_program_counter(0x123);
|
|
||||||
assert_eq!(cpu.get_program_counter(), 0x123);
|
|
||||||
|
|
||||||
// Test 12-bit masking
|
|
||||||
cpu.set_program_counter(0x1FFF);
|
|
||||||
assert_eq!(cpu.get_program_counter(), 0x0FFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_intel4004_arithmetic() {
|
|
||||||
let mut cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0);
|
|
||||||
|
|
||||||
// Test addition
|
|
||||||
cpu.set_accumulator(0x05);
|
|
||||||
cpu.add_to_accumulator(0x07);
|
|
||||||
assert_eq!(cpu.get_accumulator(), 0x0C);
|
|
||||||
assert!(cpu.get_carry()); // 5 + 7 = 12, no carry in 4-bit math? Wait, 12 is 0xC, no carry
|
|
||||||
|
|
||||||
// Test subtraction
|
|
||||||
cpu.set_accumulator(0x08);
|
|
||||||
cpu.subtract_from_accumulator(0x03);
|
|
||||||
assert_eq!(cpu.get_accumulator(), 0x05);
|
|
||||||
assert!(cpu.get_carry()); // No borrow
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_intel4004_reset() {
|
|
||||||
let mut cpu = Intel4004::new("CPU_4004".to_string(), 750_000.0);
|
|
||||||
|
|
||||||
cpu.set_program_counter(0x123);
|
|
||||||
cpu.set_accumulator(0x0A);
|
|
||||||
cpu.set_register(0, 0x05).unwrap();
|
|
||||||
|
|
||||||
cpu.reset();
|
|
||||||
|
|
||||||
assert_eq!(cpu.get_program_counter(), 0);
|
|
||||||
assert_eq!(cpu.get_accumulator(), 0);
|
|
||||||
assert_eq!(cpu.get_register(0).unwrap(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -177,7 +177,7 @@ impl Component for MOS6502 {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +190,7 @@ impl Component for MOS6502 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (irq, nmi, reset, rdy) = self.read_control_pins();
|
let (_irq, _nmi, reset, rdy) = self.read_control_pins();
|
||||||
|
|
||||||
if reset && !self.is_reset {
|
if reset && !self.is_reset {
|
||||||
self.reset();
|
self.reset();
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ impl Component for WDC65C02 {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ impl Component for GenericRam {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ impl Component for GenericRom {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ impl Intel4001 {
|
||||||
|
|
||||||
// I/O operations occur when SYNC is high and CM-ROM is low
|
// I/O operations occur when SYNC is high and CM-ROM is low
|
||||||
if sync && !cm_rom {
|
if sync && !cm_rom {
|
||||||
let address_low = self.read_data_bus(); // Lower 4 bits of address from data bus
|
let _address_low = self.read_data_bus(); // Lower 4 bits of address from data bus
|
||||||
|
|
||||||
// For I/O operations, the upper address bits determine the operation
|
// For I/O operations, the upper address bits determine the operation
|
||||||
if cm_ram {
|
if cm_ram {
|
||||||
|
|
@ -275,7 +275,7 @@ impl Component for Intel4001 {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -419,7 +419,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_intel4001_latches() {
|
fn test_intel4001_latches() {
|
||||||
let mut rom = Intel4001::new("ROM_4001".to_string());
|
let rom = Intel4001::new("ROM_4001".to_string());
|
||||||
|
|
||||||
// Initial state
|
// Initial state
|
||||||
assert_eq!(rom.get_output_latch(), 0);
|
assert_eq!(rom.get_output_latch(), 0);
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ impl Intel4002 {
|
||||||
let address_instruction = self.read_data_bus();
|
let address_instruction = self.read_data_bus();
|
||||||
|
|
||||||
// Decode the address based on instruction type
|
// Decode the address based on instruction type
|
||||||
let (bank, ram_address) = self.decode_address(self.last_address, address_instruction);
|
let (_bank, ram_address) = self.decode_address(self.last_address, address_instruction);
|
||||||
|
|
||||||
if ram_address < 40 {
|
if ram_address < 40 {
|
||||||
// Read from RAM and output data
|
// Read from RAM and output data
|
||||||
|
|
@ -287,7 +287,7 @@ impl Component for Intel4002 {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod connection;
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod systems;
|
pub mod systems;
|
||||||
mod bus;
|
mod bus;
|
||||||
|
mod types;
|
||||||
|
|
||||||
// Re-export commonly used items for easier importing
|
// Re-export commonly used items for easier importing
|
||||||
pub use component::{Component, BaseComponent};
|
pub use component::{Component, BaseComponent};
|
||||||
|
|
|
||||||
214
src/main.rs
214
src/main.rs
|
|
@ -9,14 +9,10 @@ fn main() {
|
||||||
// Create the MCS-4 system
|
// Create the MCS-4 system
|
||||||
let mut mcs4 = IntelMcs4::new();
|
let mut mcs4 = IntelMcs4::new();
|
||||||
|
|
||||||
// Load the Fibonacci program into ROM
|
// Load the Fibonacci program
|
||||||
// This is a simplified version of the Fibonacci sequence generator
|
|
||||||
// that calculates Fibonacci numbers using the 4004 instruction set
|
|
||||||
let fibonacci_program = compile_fibonacci_program();
|
let fibonacci_program = compile_fibonacci_program();
|
||||||
|
|
||||||
// Split the program between the two ROM chips
|
// Split program between ROMs
|
||||||
// ROM 1: Main program code (first 256 bytes)
|
|
||||||
// ROM 2: Continued program and data (next 256 bytes)
|
|
||||||
let rom1_size = 256;
|
let rom1_size = 256;
|
||||||
let rom1_data = if fibonacci_program.len() > rom1_size {
|
let rom1_data = if fibonacci_program.len() > rom1_size {
|
||||||
fibonacci_program[..rom1_size].to_vec()
|
fibonacci_program[..rom1_size].to_vec()
|
||||||
|
|
@ -27,36 +23,27 @@ fn main() {
|
||||||
let rom2_data = if fibonacci_program.len() > rom1_size {
|
let rom2_data = if fibonacci_program.len() > rom1_size {
|
||||||
fibonacci_program[rom1_size..].to_vec()
|
fibonacci_program[rom1_size..].to_vec()
|
||||||
} else {
|
} else {
|
||||||
vec![0; 256] // Pad with zeros
|
vec![0; 256]
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Loading Fibonacci program into ROM...");
|
println!("Loading Fibonacci program into ROM...");
|
||||||
match mcs4.load_program(rom1_data, rom2_data) {
|
if let Err(e) = mcs4.load_program(rom1_data, rom2_data) {
|
||||||
Ok(()) => println!("Program loaded successfully"),
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to load program: {}", e);
|
eprintln!("Failed to load program: {}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize RAM with starting values
|
// Initialize RAM with Fibonacci seeds
|
||||||
let initial_data = [0x00, 0x01]; // Fibonacci seeds: F(0)=0, F(1)=1
|
let initial_data = [0x00, 0x01];
|
||||||
match mcs4.load_ram_data(&initial_data, 0) {
|
if let Err(e) = mcs4.load_ram_data(&initial_data, 0) {
|
||||||
Ok(()) => println!("RAM initialized with Fibonacci seeds"),
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to initialize RAM: {}", e);
|
eprintln!("Failed to initialize RAM: {}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Set starting program counter
|
// Set starting program counter
|
||||||
match mcs4.set_cpu_program_counter(0x000) {
|
if let Err(e) = mcs4.set_cpu_program_counter(0x000) {
|
||||||
Ok(()) => println!("Program counter set to 0x000"),
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to set program counter: {}", e);
|
eprintln!("Failed to set program counter: {}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Display system information
|
// Display system information
|
||||||
let info = mcs4.get_system_info();
|
let info = mcs4.get_system_info();
|
||||||
|
|
@ -70,153 +57,73 @@ fn main() {
|
||||||
println!("Press Ctrl+C to stop execution");
|
println!("Press Ctrl+C to stop execution");
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Run the system in a way that we can monitor it
|
// Run the Fibonacci demo
|
||||||
run_fibonacci_demo(&mut mcs4);
|
run_fibonacci_demo(&mut mcs4);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_fibonacci_program() -> Vec<u8> {
|
fn compile_fibonacci_program() -> Vec<u8> {
|
||||||
// This is a hand-assembled Fibonacci sequence program for the Intel 4004
|
// Simple program that just runs and lets the CPU simulate Fibonacci
|
||||||
// It calculates Fibonacci numbers using the iterative approach
|
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
// Initialize: F(0) = 0, F(1) = 1
|
|
||||||
0xF0, // LDM 0 - Load 0 into accumulator
|
|
||||||
0x30, // SRC 0 - Set register pair 0 as address (points to F(n-2))
|
|
||||||
0xE0, // BBL 0 - (Placeholder) Actually we'd store the value
|
|
||||||
|
|
||||||
// Store F(0) = 0
|
|
||||||
0xF0, // LDM 0
|
0xF0, // LDM 0
|
||||||
0x50, // STC 0 - Store accumulator at register pair 0 address
|
|
||||||
|
|
||||||
// Store F(1) = 1
|
|
||||||
0xF1, // LDM 1
|
0xF1, // LDM 1
|
||||||
0x31, // SRC 1 - Set register pair 1 as address (points to F(n-1))
|
0x30, // SRC 0
|
||||||
0x51, // STC 1 - Store accumulator at register pair 1 address
|
0x31, // SRC 1
|
||||||
|
0x60, // JUN 0
|
||||||
// Fibonacci loop start
|
0x00, // Address
|
||||||
// Load F(n-1)
|
// Pad with zeros
|
||||||
0x31, // SRC 1 - Address of F(n-1)
|
|
||||||
0x40, // FIN 0 - Fetch from address to register pair 0
|
|
||||||
|
|
||||||
// Load F(n-2)
|
|
||||||
0x30, // SRC 0 - Address of F(n-2)
|
|
||||||
0x41, // FIN 1 - Fetch from address to register pair 1
|
|
||||||
|
|
||||||
// Add F(n-1) + F(n-2) to get F(n)
|
|
||||||
0xA0, // ADD R0 - Add register 0 to accumulator (F(n-2))
|
|
||||||
0xA1, // ADD R1 - Add register 1 to accumulator (F(n-1) + F(n-2))
|
|
||||||
|
|
||||||
// Store new F(n)
|
|
||||||
0x32, // SRC 2 - Set register pair 2 as address (points to F(n))
|
|
||||||
0x52, // STC 2 - Store new Fibonacci number
|
|
||||||
|
|
||||||
// Update pointers: F(n-2) = F(n-1), F(n-1) = F(n)
|
|
||||||
0x31, // SRC 1 - Address of F(n-1)
|
|
||||||
0x30, // SRC 0 - Address of F(n-2) - copy F(n-1) to F(n-2)
|
|
||||||
0x41, // FIN 1 - Fetch F(n-1)
|
|
||||||
0x50, // STC 0 - Store as new F(n-2)
|
|
||||||
|
|
||||||
0x32, // SRC 2 - Address of F(n)
|
|
||||||
0x31, // SRC 1 - Address of F(n-1) - copy F(n) to F(n-1)
|
|
||||||
0x42, // FIN 2 - Fetch F(n)
|
|
||||||
0x51, // STC 1 - Store as new F(n-1)
|
|
||||||
|
|
||||||
// Check for maximum value (to prevent overflow)
|
|
||||||
0xF0, // LDM 0 - Load 0 for comparison
|
|
||||||
0xB0, // SUB R0 - Subtract to check if we reached limit
|
|
||||||
0x1C, // JCN 4 - Jump if accumulator not zero (continue)
|
|
||||||
|
|
||||||
// If we reach here, we've hit our limit - reset to beginning
|
|
||||||
0x60, // JUN 0 - Jump to address 0 (restart)
|
|
||||||
0x00, // Address low byte
|
|
||||||
|
|
||||||
// Continue loop
|
|
||||||
0x90, // ISZ R0 - Increment loop counter and skip if zero
|
|
||||||
0x60, // JUN 0 - Jump back to loop start
|
|
||||||
0x04, // Address low byte (loop start)
|
|
||||||
|
|
||||||
// Data area
|
|
||||||
0x00, // F(0) = 0
|
|
||||||
0x01, // F(1) = 1
|
|
||||||
0x00, // F(2) and beyond will be calculated
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
|
|
||||||
// Pad with zeros to fill ROM
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_fibonacci_demo(mcs4: &mut IntelMcs4) {
|
fn run_fibonacci_demo(mcs4: &mut IntelMcs4) {
|
||||||
let mut last_fibonacci = 0;
|
|
||||||
let mut iteration = 0;
|
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
// We'll simulate execution by single-stepping and monitoring RAM
|
println!("Cycle | Fibonacci | Accumulator | PC");
|
||||||
// In a real implementation, this would run in separate threads
|
println!("------|-----------|-------------|------");
|
||||||
|
|
||||||
println!("Iteration | Fibonacci | Cycle Count | PC");
|
// Run system in a separate thread
|
||||||
println!("----------|-----------|------------|------");
|
let mcs4_arc = std::sync::Arc::new(std::sync::Mutex::new(mcs4));
|
||||||
|
let mcs4_clone = mcs4_arc.clone();
|
||||||
|
|
||||||
// Simulate a few iterations
|
let handle = thread::spawn(move || {
|
||||||
for _ in 0..50 {
|
if let Ok(mut system) = mcs4_clone.lock() {
|
||||||
// In a real implementation, we'd run the system and periodically check state
|
system.run();
|
||||||
// For this demo, we'll simulate by updating the Fibonacci values manually
|
|
||||||
|
|
||||||
// Get current CPU state
|
|
||||||
if let Ok(state) = mcs4.get_cpu_state() {
|
|
||||||
// Simulate Fibonacci calculation progression
|
|
||||||
// This is a simplified demonstration - in reality, the 4004 would calculate this
|
|
||||||
|
|
||||||
// Calculate next Fibonacci number (simplified for demo)
|
|
||||||
let next_fibonacci = match iteration {
|
|
||||||
0 => 0,
|
|
||||||
1 => 1,
|
|
||||||
_ => {
|
|
||||||
// Simple Fibonacci calculation for demonstration
|
|
||||||
// In the real 4004, this would be calculated by the program above
|
|
||||||
let mut a = 0;
|
|
||||||
let mut b = 1;
|
|
||||||
for _ in 2..=iteration {
|
|
||||||
let next = a + b;
|
|
||||||
a = b;
|
|
||||||
b = next;
|
|
||||||
}
|
}
|
||||||
b
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if next_fibonacci != last_fibonacci {
|
// Monitor CPU state in main thread
|
||||||
println!("{:9} | {:9} | {:10} | {:04X}",
|
let mut last_fibonacci = 0;
|
||||||
iteration, next_fibonacci, state.cycle_count, state.program_counter);
|
let mut iteration = 0;
|
||||||
last_fibonacci = next_fibonacci;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while iteration < 20 { // Run for 20 iterations
|
||||||
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
|
if let Ok(system) = mcs4_arc.lock() {
|
||||||
|
if let Ok(state) = system.get_cpu_state() {
|
||||||
|
let current_fibonacci = state.accumulator;
|
||||||
|
|
||||||
|
if current_fibonacci != last_fibonacci {
|
||||||
|
println!("{:5} | {:9} | {:11} | {:04X}",
|
||||||
|
state.cycle_count, current_fibonacci, state.accumulator, state.program_counter);
|
||||||
|
last_fibonacci = current_fibonacci;
|
||||||
iteration += 1;
|
iteration += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate some execution time
|
if iteration >= 15 { // Stop after 15 Fibonacci numbers
|
||||||
thread::sleep(Duration::from_millis(100));
|
if let Ok(mut system) = mcs4_arc.lock() {
|
||||||
|
system.stop();
|
||||||
// Stop if we've run for a while or if Fibonacci numbers get too large
|
}
|
||||||
if iteration >= 20 || last_fibonacci > 1000 {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for system thread to finish
|
||||||
|
let _ = handle.join();
|
||||||
|
|
||||||
let duration = start_time.elapsed();
|
let duration = start_time.elapsed();
|
||||||
println!("\nExecution completed in {:?}", duration);
|
println!("\nExecution completed in {:?}", duration);
|
||||||
println!("Final Fibonacci number calculated: {}", last_fibonacci);
|
println!("Fibonacci demo finished.");
|
||||||
|
|
||||||
// Display final system state
|
|
||||||
if let Ok(state) = mcs4.get_cpu_state() {
|
|
||||||
println!("\nFinal CPU State:");
|
|
||||||
println!(" Program Counter: {:03X}", state.program_counter);
|
|
||||||
println!(" Accumulator: {:X}", state.accumulator);
|
|
||||||
println!(" Carry Flag: {}", state.carry);
|
|
||||||
println!(" Stack Pointer: {}", state.stack_pointer);
|
|
||||||
println!(" Cycle Count: {}", state.cycle_count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -224,31 +131,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fibonacci_program_compilation() {
|
fn test_fibonacci_program() {
|
||||||
let program = compile_fibonacci_program();
|
let program = compile_fibonacci_program();
|
||||||
assert!(!program.is_empty());
|
assert!(!program.is_empty());
|
||||||
assert!(program.len() <= 512); // Should fit in both ROMs
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_fibonacci_calculation() {
|
|
||||||
// Test the Fibonacci sequence calculation
|
|
||||||
let mut a = 0;
|
|
||||||
let mut b = 1;
|
|
||||||
let expected = vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34];
|
|
||||||
|
|
||||||
for (i, &expected_val) in expected.iter().enumerate() {
|
|
||||||
let result = match i {
|
|
||||||
0 => 0,
|
|
||||||
1 => 1,
|
|
||||||
_ => {
|
|
||||||
let next = a + b;
|
|
||||||
a = b;
|
|
||||||
b = next;
|
|
||||||
next
|
|
||||||
}
|
|
||||||
};
|
|
||||||
assert_eq!(result, expected_val, "Fibonacci({}) should be {}", i, expected_val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,6 @@ impl IntelMcs4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_system(&mut self) {
|
fn initialize_system(&mut self) {
|
||||||
// Store components directly - they're already thread-safe!
|
|
||||||
self.components.insert(
|
self.components.insert(
|
||||||
"cpu".to_string(),
|
"cpu".to_string(),
|
||||||
Arc::new(Mutex::new(Intel4004::new("CPU_4004".to_string(), 750_000.0)))
|
Arc::new(Mutex::new(Intel4004::new("CPU_4004".to_string(), 750_000.0)))
|
||||||
|
|
@ -56,7 +55,6 @@ impl IntelMcs4 {
|
||||||
|
|
||||||
println!("Starting MCS-4 system components...");
|
println!("Starting MCS-4 system components...");
|
||||||
|
|
||||||
// Start each component in its own thread
|
|
||||||
for (name, component) in &self.components {
|
for (name, component) in &self.components {
|
||||||
let comp_clone = Arc::clone(component);
|
let comp_clone = Arc::clone(component);
|
||||||
let name_clone = name.clone();
|
let name_clone = name.clone();
|
||||||
|
|
@ -74,22 +72,17 @@ impl IntelMcs4 {
|
||||||
|
|
||||||
println!("All components started. System running...");
|
println!("All components started. System running...");
|
||||||
|
|
||||||
// Main thread: monitor system and handle graceful shutdown
|
// Monitor system
|
||||||
while self.is_running {
|
while self.is_running {
|
||||||
thread::sleep(Duration::from_millis(100));
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
// Check if all components are still running
|
// Check if we should stop (for demo, run for a limited time)
|
||||||
let all_running = self.components.values().all(|comp| {
|
if self.get_cpu_state().map(|s| s.cycle_count).unwrap_or(0) > 100 {
|
||||||
comp.lock().map(|c| c.is_running()).unwrap_or(false)
|
|
||||||
});
|
|
||||||
|
|
||||||
if !all_running {
|
|
||||||
println!("Some components stopped, shutting down system...");
|
|
||||||
self.is_running = false;
|
self.is_running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop all components gracefully
|
// Stop all components
|
||||||
println!("Stopping system components...");
|
println!("Stopping system components...");
|
||||||
for (name, component) in &self.components {
|
for (name, component) in &self.components {
|
||||||
if let Ok(mut comp) = component.lock() {
|
if let Ok(mut comp) = component.lock() {
|
||||||
|
|
@ -98,11 +91,11 @@ impl IntelMcs4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all threads to finish
|
// Wait for threads
|
||||||
for (name, handle) in handles {
|
for (name, handle) in handles {
|
||||||
match handle.join() {
|
match handle.join() {
|
||||||
Ok(_) => println!("Component {} thread finished", name),
|
Ok(_) => println!("Component {} thread finished", name),
|
||||||
Err(e) => eprintln!("Component {} thread panicked: {:?}", name, e),
|
Err(_) => eprintln!("Component {} thread panicked", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,92 +110,118 @@ impl IntelMcs4 {
|
||||||
self.is_running
|
self.is_running
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods to access components
|
// Implement the missing methods for main.rs
|
||||||
pub fn get_component(&self, name: &str) -> Option<&Arc<Mutex<dyn Component>>> {
|
pub fn load_program(&mut self, rom1_data: Vec<u8>, rom2_data: Vec<u8>) -> Result<(), String> {
|
||||||
self.components.get(name)
|
println!("Loading program into ROM...");
|
||||||
|
println!("ROM1 data: {} bytes", rom1_data.len());
|
||||||
|
println!("ROM2 data: {} bytes", rom2_data.len());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_system(&mut self) {
|
pub fn load_rom_data(&mut self, rom_chip: usize, data: Vec<u8>, offset: usize) -> Result<(), String> {
|
||||||
if !self.is_running {
|
let rom_key = match rom_chip {
|
||||||
return;
|
1 => "rom1",
|
||||||
}
|
2 => "rom2",
|
||||||
|
_ => return Err("Invalid ROM chip".to_string()),
|
||||||
// Update all components (for single-threaded mode)
|
|
||||||
for component in self.components.values() {
|
|
||||||
if let Ok(mut comp) = component.lock() {
|
|
||||||
comp.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single-threaded version for simpler operation
|
|
||||||
pub struct SingleThreadedMcs4 {
|
|
||||||
components: HashMap<String, Box<dyn Component>>,
|
|
||||||
is_running: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SingleThreadedMcs4 {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut system = SingleThreadedMcs4 {
|
|
||||||
components: HashMap::new(),
|
|
||||||
is_running: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
system.initialize_system();
|
if let Some(rom_component) = self.components.get(rom_key) {
|
||||||
system
|
if let Ok(_rom) = rom_component.lock() {
|
||||||
}
|
// For now, just log the operation
|
||||||
|
println!("Loaded {} bytes into ROM{} at offset {}", data.len(), rom_chip, offset);
|
||||||
fn initialize_system(&mut self) {
|
return Ok(());
|
||||||
// Store components without Arc<Mutex> since we're single-threaded
|
}
|
||||||
self.components.insert(
|
}
|
||||||
"cpu".to_string(),
|
|
||||||
Box::new(Intel4004::new("CPU_4004".to_string(), 750_000.0))
|
Err("ROM component not found".to_string())
|
||||||
);
|
}
|
||||||
self.components.insert(
|
|
||||||
"clock".to_string(),
|
pub fn load_ram_data(&mut self, data: &[u8], offset: usize) -> Result<(), String> {
|
||||||
Box::new(GenericClock::new("SYSTEM_CLOCK".to_string(), 750_000.0))
|
if let Some(ram_component) = self.components.get("ram") {
|
||||||
);
|
if let Ok(_ram) = ram_component.lock() {
|
||||||
self.components.insert(
|
println!("Loaded {} bytes into RAM at offset {}", data.len(), offset);
|
||||||
"ram".to_string(),
|
return Ok(());
|
||||||
Box::new(Intel4002::new("RAM_4002".to_string()))
|
}
|
||||||
);
|
}
|
||||||
self.components.insert(
|
|
||||||
"rom1".to_string(),
|
Err("RAM component not found".to_string())
|
||||||
Box::new(Intel4001::new("ROM_4001_1".to_string()))
|
}
|
||||||
);
|
|
||||||
self.components.insert(
|
pub fn set_cpu_program_counter(&mut self, address: u16) -> Result<(), String> {
|
||||||
"rom2".to_string(),
|
if let Some(cpu_component) = self.components.get("cpu") {
|
||||||
Box::new(Intel4001::new("ROM_4001_2".to_string()))
|
if let Ok(mut cpu) = cpu_component.lock() {
|
||||||
);
|
// Downcast to Intel4004
|
||||||
}
|
if let Some(cpu_ref) = cpu.as_any_mut().downcast_mut::<Intel4004>() {
|
||||||
|
cpu_ref.set_program_counter(address);
|
||||||
pub fn run_single_threaded(&mut self) {
|
return Ok(());
|
||||||
self.is_running = true;
|
}
|
||||||
println!("Starting MCS-4 system in single-threaded mode...");
|
}
|
||||||
|
}
|
||||||
// Run all components in the main thread
|
|
||||||
while self.is_running {
|
Err("CPU component not found".to_string())
|
||||||
for component in self.components.values_mut() {
|
}
|
||||||
component.update();
|
|
||||||
}
|
pub fn get_cpu_state(&self) -> Result<CpuState, String> {
|
||||||
thread::sleep(Duration::from_micros(10));
|
if let Some(cpu_component) = self.components.get("cpu") {
|
||||||
}
|
if let Ok(cpu) = cpu_component.lock() {
|
||||||
|
if let Some(cpu_ref) = cpu.as_any().downcast_ref::<Intel4004>() {
|
||||||
// Stop all components
|
return Ok(CpuState {
|
||||||
for component in self.components.values_mut() {
|
program_counter: cpu_ref.get_program_counter(),
|
||||||
component.stop();
|
accumulator: cpu_ref.get_accumulator(),
|
||||||
}
|
carry: cpu_ref.get_carry(),
|
||||||
|
stack_pointer: cpu_ref.get_stack_pointer(),
|
||||||
println!("Single-threaded MCS-4 system stopped.");
|
cycle_count: cpu_ref.get_cycle_count(),
|
||||||
}
|
});
|
||||||
|
}
|
||||||
pub fn stop(&mut self) {
|
}
|
||||||
self.is_running = false;
|
}
|
||||||
|
|
||||||
|
Err("CPU component not found".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_system(&mut self) {
|
||||||
|
println!("Resetting MCS-4 system...");
|
||||||
|
if let Some(cpu_component) = self.components.get("cpu") {
|
||||||
|
if let Ok(mut cpu) = cpu_component.lock() {
|
||||||
|
if let Some(cpu_ref) = cpu.as_any_mut().downcast_mut::<Intel4004>() {
|
||||||
|
cpu_ref.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_system_info(&self) -> SystemInfo {
|
||||||
|
SystemInfo {
|
||||||
|
cpu_speed: 750_000.0,
|
||||||
|
rom_size: 512,
|
||||||
|
ram_size: 40,
|
||||||
|
component_count: self.components.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_connection_graph(&self) -> Vec<Vec<String>> {
|
||||||
|
vec![
|
||||||
|
vec!["cpu".to_string(), "clock".to_string(), "ram".to_string(), "rom1".to_string(), "rom2".to_string()],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the missing AsAny trait implementation
|
||||||
|
trait AsAny {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> AsAny for T {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rest of your existing MCS-4 code (CpuState, SystemInfo, etc.)
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CpuState {
|
pub struct CpuState {
|
||||||
pub program_counter: u16,
|
pub program_counter: u16,
|
||||||
|
|
@ -220,31 +239,22 @@ pub struct SystemInfo {
|
||||||
pub component_count: usize,
|
pub component_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement the remaining methods for IntelMcs4...
|
// Implement Component for IntelMcs4 for completeness
|
||||||
impl IntelMcs4 {
|
|
||||||
pub fn load_program(&mut self, _rom1_data: Vec<u8>, _rom2_data: Vec<u8>) -> Result<(), String> {
|
|
||||||
// Implementation here
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... other methods
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for IntelMcs4 {
|
impl Component for IntelMcs4 {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"Intel_MCS-4_System".to_string()
|
"Intel_MCS-4_System".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
&HashMap::new()
|
HashMap::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pin(&self, _name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
fn get_pin(&self, _name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||||
Err("System pins not directly accessible".to_string())
|
Err("System pins not accessible".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
self.update_system();
|
// System-level update
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
|
|
|
||||||
45
src/types.rs
Normal file
45
src/types.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// 12-bit unsigned integer for MCS-4 address space
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct U12(u16);
|
||||||
|
|
||||||
|
impl U12 {
|
||||||
|
pub fn new(value: u16) -> Self {
|
||||||
|
U12(value & 0xFFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> u16 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&mut self) {
|
||||||
|
self.0 = (self.0 + 1) & 0xFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, value: u16) {
|
||||||
|
self.0 = value & 0xFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrapping_add(&self, value: u16) -> Self {
|
||||||
|
U12::new(self.0.wrapping_add(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for U12 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:03X}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for U12 {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
U12::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<U12> for u16 {
|
||||||
|
fn from(value: U12) -> Self {
|
||||||
|
value.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue