Rust code formatter
Added threaded RunnableComponent trait Accurate, threaded implementation of the 4001 ROM
This commit is contained in:
parent
01117af411
commit
cff459d2b5
22 changed files with 488 additions and 197 deletions
12
src/bus.rs
12
src/bus.rs
|
|
@ -4,7 +4,7 @@ use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::component::{BaseComponent, Component};
|
use crate::component::{BaseComponent, Component};
|
||||||
use crate::pin::{Pin, PinValue, DriveStrength};
|
use crate::pin::{DriveStrength, Pin, PinValue};
|
||||||
|
|
||||||
pub struct GenericBus {
|
pub struct GenericBus {
|
||||||
base: BaseComponent,
|
base: BaseComponent,
|
||||||
|
|
@ -105,7 +105,8 @@ impl GenericBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the strongest driver
|
// Find the strongest driver
|
||||||
let max_strength = drivers.iter()
|
let max_strength = drivers
|
||||||
|
.iter()
|
||||||
.map(|(_, strength)| *strength)
|
.map(|(_, strength)| *strength)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(DriveStrength::HighImpedance);
|
.unwrap_or(DriveStrength::HighImpedance);
|
||||||
|
|
@ -115,7 +116,8 @@ impl GenericBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get values from strongest drivers
|
// Get values from strongest drivers
|
||||||
let strong_drivers: Vec<PinValue> = drivers.iter()
|
let strong_drivers: Vec<PinValue> = drivers
|
||||||
|
.iter()
|
||||||
.filter(|(_, strength)| *strength == max_strength)
|
.filter(|(_, strength)| *strength == max_strength)
|
||||||
.map(|(value, _)| *value)
|
.map(|(value, _)| *value)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -141,7 +143,7 @@ impl GenericBus {
|
||||||
pin_guard.set_driver_with_strength(
|
pin_guard.set_driver_with_strength(
|
||||||
Some(self.base.get_name().to_string()),
|
Some(self.base.get_name().to_string()),
|
||||||
self.bus_value,
|
self.bus_value,
|
||||||
DriveStrength::Standard
|
DriveStrength::Standard,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +271,7 @@ impl ActiveBus {
|
||||||
pin_guard.set_driver_with_strength(
|
pin_guard.set_driver_with_strength(
|
||||||
Some(self.base.name() + "_pattern"),
|
Some(self.base.name() + "_pattern"),
|
||||||
value,
|
value,
|
||||||
DriveStrength::Strong
|
DriveStrength::Strong,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex, atomic::{AtomicBool, Ordering}};
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -15,7 +18,16 @@ pub trait Component: Send + Sync {
|
||||||
fn stop(&mut self);
|
fn stop(&mut self);
|
||||||
fn is_running(&self) -> bool;
|
fn is_running(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
pub trait RunnableComponent: Component + Send + 'static {
|
||||||
|
fn spawn_in_thread(mut self) -> thread::JoinHandle<()>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
thread::spawn(move || {
|
||||||
|
self.run();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
// BaseComponent uses AtomicBool for thread-safe state
|
// BaseComponent uses AtomicBool for thread-safe state
|
||||||
pub struct BaseComponent {
|
pub struct BaseComponent {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -44,12 +56,18 @@ impl BaseComponent {
|
||||||
self.running.store(running, Ordering::SeqCst);
|
self.running.store(running, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_pin_map(pin_names: &[&str], component_name: &str) -> HashMap<String, Arc<Mutex<Pin>>> {
|
pub fn create_pin_map(
|
||||||
|
pin_names: &[&str],
|
||||||
|
component_name: &str,
|
||||||
|
) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
let mut pins = HashMap::new();
|
let mut pins = HashMap::new();
|
||||||
for pin_name in pin_names {
|
for pin_name in pin_names {
|
||||||
pins.insert(
|
pins.insert(
|
||||||
pin_name.to_string(),
|
pin_name.to_string(),
|
||||||
Arc::new(Mutex::new(Pin::new(format!("{}_{}", component_name, pin_name))))
|
Arc::new(Mutex::new(Pin::new(format!(
|
||||||
|
"{}_{}",
|
||||||
|
component_name, pin_name
|
||||||
|
)))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pins
|
pins
|
||||||
|
|
@ -66,7 +84,8 @@ impl Component for BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||||
self.pins.get(name)
|
self.pins
|
||||||
|
.get(name)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| format!("Pin {} not found", name))
|
.ok_or_else(|| format!("Pin {} not found", name))
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +107,8 @@ impl Component for BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.is_running()
|
self.running.load(Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RunnableComponent for BaseComponent {}
|
||||||
|
|
|
||||||
|
|
@ -232,8 +232,13 @@ impl GenericClock {
|
||||||
let half_period = self.half_period;
|
let half_period = self.half_period;
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
for i in 0..count * 2 { // Each cycle has two transitions
|
for i in 0..count * 2 {
|
||||||
let state = if i % 2 == 0 { PinValue::High } else { PinValue::Low };
|
// Each cycle has two transitions
|
||||||
|
let state = if i % 2 == 0 {
|
||||||
|
PinValue::High
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(mut pin_guard) = clock_pin.lock() {
|
if let Ok(mut pin_guard) = clock_pin.lock() {
|
||||||
pin_guard.set_driver(Some(clock_name.clone()), state);
|
pin_guard.set_driver(Some(clock_name.clone()), state);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::component::{BaseComponent, Component};
|
use crate::component::{BaseComponent, Component, RunnableComponent};
|
||||||
use crate::pin::{Pin, PinValue};
|
use crate::pin::{Pin, PinValue};
|
||||||
use crate::types::U12;
|
use crate::types::U12;
|
||||||
|
|
||||||
|
|
@ -44,8 +44,7 @@ pub struct Intel4004 {
|
||||||
impl Intel4004 {
|
impl Intel4004 {
|
||||||
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",
|
"D0", "D1", "D2", "D3", "SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2",
|
||||||
"SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let pin_strings: Vec<String> = pin_names.iter().map(|s| s.to_string()).collect();
|
let pin_strings: Vec<String> = pin_names.iter().map(|s| s.to_string()).collect();
|
||||||
|
|
@ -133,7 +132,8 @@ impl Intel4004 {
|
||||||
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)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard
|
||||||
|
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -291,6 +291,7 @@ impl Component for Intel4004 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
|
// Time-slice model: run in a loop calling update() each cycle
|
||||||
self.base.set_running(true);
|
self.base.set_running(true);
|
||||||
self.reset();
|
self.reset();
|
||||||
|
|
||||||
|
|
@ -312,3 +313,7 @@ impl Component for Intel4004 {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RunnableComponent for Intel4004 {
|
||||||
|
// No custom run_loop needed - uses default Component::run() method
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// CPU components module
|
// CPU components module
|
||||||
|
mod cpu_traits;
|
||||||
|
pub mod intel_4004;
|
||||||
pub mod mos_6502;
|
pub mod mos_6502;
|
||||||
pub mod wdc_65c02;
|
pub mod wdc_65c02;
|
||||||
pub mod intel_4004;
|
|
||||||
mod cpu_traits;
|
|
||||||
|
|
||||||
// Re-export the CPU types
|
// Re-export the CPU types
|
||||||
|
pub use intel_4004::Intel4004;
|
||||||
pub use mos_6502::MOS6502;
|
pub use mos_6502::MOS6502;
|
||||||
pub use wdc_65c02::WDC65C02;
|
pub use wdc_65c02::WDC65C02;
|
||||||
pub use intel_4004::Intel4004;
|
|
||||||
|
|
@ -26,7 +26,8 @@ pub struct MOS6502 {
|
||||||
impl MOS6502 {
|
impl MOS6502 {
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
let pin_names = vec![
|
let pin_names = vec![
|
||||||
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", // 16 address lines
|
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11", "A12", "A13",
|
||||||
|
"A14", "A15", // 16 address lines
|
||||||
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", // 8 data lines
|
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", // 8 data lines
|
||||||
"RW", // Read/Write
|
"RW", // Read/Write
|
||||||
"IRQ", // Interrupt Request
|
"IRQ", // Interrupt Request
|
||||||
|
|
@ -73,7 +74,11 @@ impl MOS6502 {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
let bit_value = (address >> i) & 1;
|
let bit_value = (address >> i) & 1;
|
||||||
let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low };
|
let pin_value = if bit_value == 1 {
|
||||||
|
PinValue::High
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
};
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +90,11 @@ impl MOS6502 {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
let bit_value = (data >> i) & 1;
|
let bit_value = (data >> i) & 1;
|
||||||
let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low };
|
let pin_value = if bit_value == 1 {
|
||||||
|
PinValue::High
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
};
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ impl GenericRam {
|
||||||
if offset + data.len() > self.memory.len() {
|
if offset + data.len() > self.memory.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Data exceeds RAM capacity: offset {} + data length {} > RAM size {}",
|
"Data exceeds RAM capacity: offset {} + data length {} > RAM size {}",
|
||||||
offset, data.len(), self.memory.len()
|
offset,
|
||||||
|
data.len(),
|
||||||
|
self.memory.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +84,9 @@ impl GenericRam {
|
||||||
if offset + data.len() > self.memory.len() {
|
if offset + data.len() > self.memory.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Data exceeds RAM capacity: offset {} + data length {} > RAM size {}",
|
"Data exceeds RAM capacity: offset {} + data length {} > RAM size {}",
|
||||||
offset, data.len(), self.memory.len()
|
offset,
|
||||||
|
data.len(),
|
||||||
|
self.memory.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,7 +210,8 @@ impl GenericRam {
|
||||||
for i in 0..self.data_width {
|
for i in 0..self.data_width {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard
|
||||||
|
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,29 @@ impl GenericRom {
|
||||||
|
|
||||||
// Create address pins (A0, A1, A2, ...)
|
// Create address pins (A0, A1, A2, ...)
|
||||||
for i in 0..address_width {
|
for i in 0..address_width {
|
||||||
pins.insert(format!("A{}", i), Arc::new(Mutex::new(Pin::new(format!("{}_A{}", name, i)))));
|
pins.insert(
|
||||||
|
format!("A{}", i),
|
||||||
|
Arc::new(Mutex::new(Pin::new(format!("{}_A{}", name, i)))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create data pins (D0, D1, D2, ...)
|
// Create data pins (D0, D1, D2, ...)
|
||||||
for i in 0..data_width {
|
for i in 0..data_width {
|
||||||
pins.insert(format!("D{}", i), Arc::new(Mutex::new(Pin::new(format!("{}_D{}", name, i)))));
|
pins.insert(
|
||||||
|
format!("D{}", i),
|
||||||
|
Arc::new(Mutex::new(Pin::new(format!("{}_D{}", name, i)))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control pins
|
// Control pins
|
||||||
pins.insert("CS".to_string(), Arc::new(Mutex::new(Pin::new(format!("{}_CS", name)))));
|
pins.insert(
|
||||||
pins.insert("OE".to_string(), Arc::new(Mutex::new(Pin::new(format!("{}_OE", name)))));
|
"CS".to_string(),
|
||||||
|
Arc::new(Mutex::new(Pin::new(format!("{}_CS", name)))),
|
||||||
|
);
|
||||||
|
pins.insert(
|
||||||
|
"OE".to_string(),
|
||||||
|
Arc::new(Mutex::new(Pin::new(format!("{}_OE", name)))),
|
||||||
|
);
|
||||||
|
|
||||||
let memory = vec![0u8; size];
|
let memory = vec![0u8; size];
|
||||||
|
|
||||||
|
|
@ -70,7 +82,11 @@ impl GenericRom {
|
||||||
Err(_) => Err("Invalid hex data".to_string()),
|
Err(_) => Err("Invalid hex data".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn load_from_binary_file<P: AsRef<Path>>(&mut self, path: P, offset: usize) -> Result<(), String> {
|
pub fn load_from_binary_file<P: AsRef<Path>>(
|
||||||
|
&mut self,
|
||||||
|
path: P,
|
||||||
|
offset: usize,
|
||||||
|
) -> Result<(), String> {
|
||||||
let path_ref = path.as_ref();
|
let path_ref = path.as_ref();
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
|
|
@ -80,14 +96,18 @@ impl GenericRom {
|
||||||
|
|
||||||
// Check if offset is within bounds
|
// Check if offset is within bounds
|
||||||
if offset >= self.memory.len() {
|
if offset >= self.memory.len() {
|
||||||
return Err(format!("Offset {} exceeds ROM size {}", offset, self.memory.len()));
|
return Err(format!(
|
||||||
|
"Offset {} exceeds ROM size {}",
|
||||||
|
offset,
|
||||||
|
self.memory.len()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file = File::open(path_ref)
|
let mut file = File::open(path_ref).map_err(|e| format!("Failed to open file: {}", e))?;
|
||||||
.map_err(|e| format!("Failed to open file: {}", e))?;
|
|
||||||
|
|
||||||
// Get file size
|
// Get file size
|
||||||
let metadata = file.metadata()
|
let metadata = file
|
||||||
|
.metadata()
|
||||||
.map_err(|e| format!("Failed to get file metadata: {}", e))?;
|
.map_err(|e| format!("Failed to get file metadata: {}", e))?;
|
||||||
let file_size = metadata.len() as usize;
|
let file_size = metadata.len() as usize;
|
||||||
|
|
||||||
|
|
@ -95,7 +115,9 @@ impl GenericRom {
|
||||||
if offset + file_size > self.memory.len() {
|
if offset + file_size > self.memory.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"File too large: offset {} + file size {} > ROM size {}",
|
"File too large: offset {} + file size {} > ROM size {}",
|
||||||
offset, file_size, self.memory.len()
|
offset,
|
||||||
|
file_size,
|
||||||
|
self.memory.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +136,9 @@ impl GenericRom {
|
||||||
if offset + data.len() > self.memory.len() {
|
if offset + data.len() > self.memory.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Data exceeds ROM capacity: offset {} + data length {} > ROM size {}",
|
"Data exceeds ROM capacity: offset {} + data length {} > ROM size {}",
|
||||||
offset, data.len(), self.memory.len()
|
offset,
|
||||||
|
data.len(),
|
||||||
|
self.memory.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +146,6 @@ impl GenericRom {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn read_address(&self) -> u32 {
|
fn read_address(&self) -> u32 {
|
||||||
let mut address = 0;
|
let mut address = 0;
|
||||||
|
|
||||||
|
|
@ -170,7 +193,10 @@ impl GenericRom {
|
||||||
for i in 0..self.data_width {
|
for i in 0..self.data_width {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard.set_driver(
|
||||||
|
Some(self.base.get_name().parse().unwrap()),
|
||||||
|
PinValue::HighZ,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +253,10 @@ impl Component for GenericRom {
|
||||||
for i in 0..self.data_width {
|
for i in 0..self.data_width {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard.set_driver(
|
||||||
|
Some(self.base.get_name().parse().unwrap()),
|
||||||
|
PinValue::HighZ,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +266,10 @@ impl Component for GenericRom {
|
||||||
for i in 0..self.data_width {
|
for i in 0..self.data_width {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard.set_driver(
|
||||||
|
Some(self.base.get_name().parse().unwrap()),
|
||||||
|
PinValue::HighZ,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +296,8 @@ impl Component for GenericRom {
|
||||||
for i in 0..self.data_width {
|
for i in 0..self.data_width {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard
|
||||||
|
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,9 +310,9 @@ impl Component for GenericRom {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rom_creation() {
|
fn test_rom_creation() {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::component::{BaseComponent, Component};
|
use crate::component::{BaseComponent, Component, RunnableComponent};
|
||||||
use crate::pin::{Pin, PinValue};
|
use crate::pin::{Pin, PinValue};
|
||||||
|
|
||||||
/// Intel 4001 - 256-byte ROM with integrated I/O
|
/// Intel 4001 - 256-byte ROM with integrated I/O
|
||||||
|
|
@ -17,6 +16,13 @@ pub struct Intel4001 {
|
||||||
output_latch: u8,
|
output_latch: u8,
|
||||||
input_latch: u8,
|
input_latch: u8,
|
||||||
io_mode: IoMode,
|
io_mode: IoMode,
|
||||||
|
// Clock edge detection
|
||||||
|
prev_phi1: PinValue,
|
||||||
|
prev_phi2: PinValue,
|
||||||
|
// Access latency modeling
|
||||||
|
address_latched: bool,
|
||||||
|
address_latch_time: Option<Instant>,
|
||||||
|
data_available: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
@ -31,18 +37,26 @@ impl Intel4001 {
|
||||||
// Intel 4001 has 256 bytes of ROM
|
// Intel 4001 has 256 bytes of ROM
|
||||||
let rom_size = 256;
|
let rom_size = 256;
|
||||||
|
|
||||||
// Intel 4001 pinout:
|
// Intel 4001 pinout (per datasheet):
|
||||||
// - 10 address pins (A0-A9, but only A0-A7 used for 256-byte addressing)
|
|
||||||
// - 4 data pins (D0-D3) for multiplexed address/data
|
// - 4 data pins (D0-D3) for multiplexed address/data
|
||||||
// - 4 I/O pins (IO0-IO3)
|
// - 4 I/O pins (IO0-IO3)
|
||||||
// - Control pins: SYNC, CM-ROM, CM-RAM, RESET
|
// - Control pins: SYNC, RESET, CM, CI
|
||||||
|
// - Clock pins: Φ1, Φ2 (two-phase clock from 4004 CPU)
|
||||||
|
//
|
||||||
|
// Control pin behavior:
|
||||||
|
// - SYNC: Marks start of instruction cycle
|
||||||
|
// - RESET: Clears internal state
|
||||||
|
// - CM: Chip select for ROM vs RAM
|
||||||
|
// - CI: Distinguishes I/O (LOW) vs ROM access (HIGH) when CM-ROM is HIGH
|
||||||
let pin_names = vec![
|
let pin_names = vec![
|
||||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||||
"IO0", "IO1", "IO2", "IO3", // I/O pins
|
"IO0", "IO1", "IO2", "IO3", // I/O pins
|
||||||
"SYNC", // Sync signal
|
"SYNC", // Sync signal
|
||||||
"CM_ROM", // ROM Chip Select
|
"CM", // CM-ROM: ROM/RAM Chip Select
|
||||||
"CM_RAM", // RAM Chip Select (for system configuration)
|
"CI", // CM-RAM: RAM Chip Select (I/O vs ROM access)
|
||||||
"RESET", // Reset
|
"RESET", // Reset
|
||||||
|
"PHI1", // Clock phase 1
|
||||||
|
"PHI2", // Clock phase 2
|
||||||
];
|
];
|
||||||
|
|
||||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||||
|
|
@ -57,6 +71,11 @@ impl Intel4001 {
|
||||||
output_latch: 0,
|
output_latch: 0,
|
||||||
input_latch: 0,
|
input_latch: 0,
|
||||||
io_mode: IoMode::Input,
|
io_mode: IoMode::Input,
|
||||||
|
prev_phi1: PinValue::Low,
|
||||||
|
prev_phi2: PinValue::Low,
|
||||||
|
address_latched: false,
|
||||||
|
address_latch_time: None,
|
||||||
|
data_available: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +83,9 @@ impl Intel4001 {
|
||||||
if offset + data.len() > self.memory.len() {
|
if offset + data.len() > self.memory.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Data exceeds ROM capacity: offset {} + data length {} > ROM size {}",
|
"Data exceeds ROM capacity: offset {} + data length {} > ROM size {}",
|
||||||
offset, data.len(), self.memory.len()
|
offset,
|
||||||
|
data.len(),
|
||||||
|
self.memory.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,26 +170,87 @@ impl Intel4001 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tri_state_data_bus(&self) {
|
fn tri_state_data_bus(&self) {
|
||||||
|
// CRITICAL: Tri-state data bus to avoid contention with other chips
|
||||||
|
// The 4001 must be high-Z whenever not actively driving valid data
|
||||||
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)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard
|
||||||
|
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn should_drive_data_bus(&self) -> bool {
|
||||||
|
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
|
||||||
|
// ROM drives data bus ONLY when all conditions are met:
|
||||||
|
sync && cm_rom && cm_ram && self.data_available
|
||||||
|
}
|
||||||
|
|
||||||
fn tri_state_io_pins(&self) {
|
fn tri_state_io_pins(&self) {
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
if let Ok(pin) = self.base.get_pin(&format!("IO{}", i)) {
|
if let Ok(pin) = self.base.get_pin(&format!("IO{}", i)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard
|
||||||
|
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_clock_pins(&self) -> (PinValue, PinValue) {
|
||||||
|
let phi1 = if let Ok(pin) = self.base.get_pin("PHI1") {
|
||||||
|
if let Ok(pin_guard) = pin.lock() {
|
||||||
|
pin_guard.read()
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
};
|
||||||
|
|
||||||
|
let phi2 = if let Ok(pin) = self.base.get_pin("PHI2") {
|
||||||
|
if let Ok(pin_guard) = pin.lock() {
|
||||||
|
pin_guard.read()
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PinValue::Low
|
||||||
|
};
|
||||||
|
|
||||||
|
(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
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_phi2_rising_edge(&self) -> bool {
|
||||||
|
let (_, phi2) = self.read_clock_pins();
|
||||||
|
phi2 == PinValue::High && self.prev_phi2 == PinValue::Low
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_phi2_falling_edge(&self) -> bool {
|
||||||
|
let (_, phi2) = self.read_clock_pins();
|
||||||
|
phi2 == PinValue::Low && self.prev_phi2 == PinValue::High
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_clock_states(&mut self) {
|
||||||
|
let (phi1, phi2) = self.read_clock_pins();
|
||||||
|
self.prev_phi1 = phi1;
|
||||||
|
self.prev_phi2 = phi2;
|
||||||
|
}
|
||||||
|
|
||||||
fn read_control_pins(&self) -> (bool, bool, bool, bool) {
|
fn read_control_pins(&self) -> (bool, bool, bool, bool) {
|
||||||
|
// SYNC: Marks the start of an instruction cycle
|
||||||
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() {
|
||||||
pin_guard.read() == PinValue::High
|
pin_guard.read() == PinValue::High
|
||||||
|
|
@ -179,7 +261,8 @@ impl Intel4001 {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let cm_rom = if let Ok(pin) = self.base.get_pin("CM_ROM") {
|
// CM-ROM: Chip select for ROM vs RAM (HIGH = ROM access)
|
||||||
|
let cm_rom = if let Ok(pin) = self.base.get_pin("CM") {
|
||||||
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 {
|
||||||
|
|
@ -189,7 +272,9 @@ impl Intel4001 {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let cm_ram = if let Ok(pin) = self.base.get_pin("CM_RAM") {
|
// CM-RAM: Distinguishes I/O vs ROM access when CM-ROM is HIGH
|
||||||
|
// HIGH = ROM access, LOW = I/O access
|
||||||
|
let cm_ram = if let Ok(pin) = self.base.get_pin("CI") {
|
||||||
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 {
|
||||||
|
|
@ -213,12 +298,18 @@ impl Intel4001 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_reset(&mut self) {
|
fn handle_reset(&mut self) {
|
||||||
if self.read_control_pins().3 { // RESET is high
|
let (_, _, _, reset) = self.read_control_pins();
|
||||||
|
if reset {
|
||||||
|
// RESET is high - clear internal state
|
||||||
self.output_latch = 0;
|
self.output_latch = 0;
|
||||||
self.input_latch = 0;
|
self.input_latch = 0;
|
||||||
self.io_mode = IoMode::Input;
|
self.io_mode = IoMode::Input;
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
self.tri_state_io_pins();
|
self.tri_state_io_pins();
|
||||||
|
// Reset access latency state
|
||||||
|
self.address_latched = false;
|
||||||
|
self.address_latch_time = None;
|
||||||
|
self.data_available = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,23 +338,39 @@ impl Intel4001 {
|
||||||
fn handle_memory_operation(&mut self) -> bool {
|
fn handle_memory_operation(&mut self) -> bool {
|
||||||
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
|
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
|
||||||
|
|
||||||
// Memory operations occur when SYNC is high and CM-ROM is high
|
// TRI-STATE RULE: ROM only drives data bus when ALL of these are true:
|
||||||
if sync && cm_rom {
|
// - SYNC=1 (instruction cycle active)
|
||||||
if cm_ram {
|
// - CM-ROM=1 (ROM chip selected)
|
||||||
// Second cycle of memory read - output data
|
// - CM-RAM=1 (data phase, not address phase)
|
||||||
|
// - data_available=true (access latency has elapsed)
|
||||||
|
//
|
||||||
|
// ALL other cases MUST be high-Z to avoid bus contention with CPU/other chips
|
||||||
|
|
||||||
|
if self.should_drive_data_bus() {
|
||||||
|
// EXACT CONDITIONS: SYNC=1, CM-ROM=1, CM-RAM=1, and data available
|
||||||
|
// This is the ONLY case where ROM drives the data bus
|
||||||
let address = self.last_address;
|
let address = self.last_address;
|
||||||
if (address as usize) < self.memory.len() {
|
if (address as usize) < self.memory.len() {
|
||||||
let data = self.memory[address as usize];
|
let data = self.memory[address as usize];
|
||||||
self.write_data_bus(data);
|
self.write_data_bus(data);
|
||||||
return true;
|
return true; // Successfully drove the bus
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// First cycle of memory operation - latch address
|
|
||||||
|
// ALL other cases: tri-state the data bus
|
||||||
|
self.tri_state_data_bus();
|
||||||
|
|
||||||
|
// Handle address latching when in address phase (but still tri-state)
|
||||||
|
if sync && cm_rom && !cm_ram {
|
||||||
|
// First cycle: CPU outputs address, ROM latches it
|
||||||
let address_low = self.read_data_bus();
|
let address_low = self.read_data_bus();
|
||||||
self.last_address = address_low as u16;
|
self.last_address = address_low as u16;
|
||||||
// Tri-state during address phase
|
self.address_latched = true;
|
||||||
self.tri_state_data_bus();
|
self.address_latch_time = Some(Instant::now());
|
||||||
}
|
self.data_available = false;
|
||||||
|
} else if !self.address_latched {
|
||||||
|
// Reset data available flag when not in active operation
|
||||||
|
self.data_available = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
@ -284,11 +391,28 @@ impl Component for Intel4001 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
// Respect access timing
|
// Only update on PHI1 rising edge (synchronous with CPU clock)
|
||||||
if self.last_access.elapsed() < self.access_time {
|
if !self.is_phi1_rising_edge() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update clock states for next edge detection
|
||||||
|
let (phi1, phi2) = self.read_clock_pins();
|
||||||
|
self.prev_phi1 = phi1;
|
||||||
|
self.prev_phi2 = phi2;
|
||||||
|
|
||||||
|
// Check if we have a latched address and need to make data available
|
||||||
|
if self.address_latched {
|
||||||
|
if let Some(latch_time) = self.address_latch_time {
|
||||||
|
if latch_time.elapsed() >= self.access_time {
|
||||||
|
// Access latency has elapsed, data is now available
|
||||||
|
self.data_available = true;
|
||||||
|
self.address_latched = false;
|
||||||
|
self.address_latch_time = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.handle_reset();
|
self.handle_reset();
|
||||||
|
|
||||||
// Handle I/O operations (higher priority than memory operations)
|
// Handle I/O operations (higher priority than memory operations)
|
||||||
|
|
@ -297,23 +421,28 @@ impl Component for Intel4001 {
|
||||||
// Handle memory operations
|
// Handle memory operations
|
||||||
let memory_accessed = self.handle_memory_operation();
|
let memory_accessed = self.handle_memory_operation();
|
||||||
|
|
||||||
// If no memory operation occurred, ensure data bus is tri-stated when not active
|
// Additional safety: ensure data bus is tri-stated when ROM is not actively driving
|
||||||
if !memory_accessed {
|
// This handles any edge cases not covered in handle_memory_operation
|
||||||
let (sync, cm_rom, _, _) = self.read_control_pins();
|
if !memory_accessed && !self.should_drive_data_bus() {
|
||||||
if !sync || !cm_rom {
|
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.last_access = Instant::now();
|
self.last_access = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
|
// Time-slice model: run in a loop calling update() each cycle
|
||||||
self.base.set_running(true);
|
self.base.set_running(true);
|
||||||
|
|
||||||
|
// Initialize clock states
|
||||||
|
let (phi1, phi2) = self.read_clock_pins();
|
||||||
|
self.prev_phi1 = phi1;
|
||||||
|
self.prev_phi2 = phi2;
|
||||||
|
|
||||||
while self.is_running() {
|
while self.is_running() {
|
||||||
self.update();
|
self.update();
|
||||||
thread::sleep(Duration::from_micros(10));
|
// Small delay to prevent busy waiting when no clock is present
|
||||||
|
std::thread::sleep(std::time::Duration::from_micros(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,6 +452,11 @@ impl Component for Intel4001 {
|
||||||
// Tri-state all outputs when stopped
|
// Tri-state all outputs when stopped
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
self.tri_state_io_pins();
|
self.tri_state_io_pins();
|
||||||
|
|
||||||
|
// Reset access latency state
|
||||||
|
self.address_latched = false;
|
||||||
|
self.address_latch_time = None;
|
||||||
|
self.data_available = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
|
|
@ -330,6 +464,8 @@ impl Component for Intel4001 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RunnableComponent for Intel4001 {}
|
||||||
|
|
||||||
// Intel 4001 specific methods
|
// Intel 4001 specific methods
|
||||||
impl Intel4001 {
|
impl Intel4001 {
|
||||||
pub fn get_rom_size(&self) -> usize {
|
pub fn get_rom_size(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,8 @@ impl Intel4002 {
|
||||||
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)) {
|
||||||
if let Ok(mut pin_guard) = pin.lock() {
|
if let Ok(mut pin_guard) = pin.lock() {
|
||||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
pin_guard
|
||||||
|
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +232,8 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
pub mod intel_4002;
|
|
||||||
pub mod intel_4001;
|
|
||||||
pub mod generic_rom;
|
|
||||||
mod generic_ram;
|
mod generic_ram;
|
||||||
|
pub mod generic_rom;
|
||||||
|
pub mod intel_4001;
|
||||||
|
pub mod intel_4002;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
pub mod cpu;
|
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
|
pub mod cpu;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
use crate::pin::Pin;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use crate::pin::Pin;
|
|
||||||
|
|
||||||
/// Manages electrical connections between pins
|
/// Manages electrical connections between pins
|
||||||
pub struct ConnectionManager {
|
pub struct ConnectionManager {
|
||||||
|
|
@ -25,35 +25,49 @@ impl ConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect two pins bidirectionally
|
/// Connect two pins bidirectionally
|
||||||
pub fn connect_pins(&mut self, pin1: Arc<Mutex<Pin>>, pin2: Arc<Mutex<Pin>>) -> Result<(), String> {
|
pub fn connect_pins(
|
||||||
|
&mut self,
|
||||||
|
pin1: Arc<Mutex<Pin>>,
|
||||||
|
pin2: Arc<Mutex<Pin>>,
|
||||||
|
) -> Result<(), String> {
|
||||||
let pin1_name = {
|
let pin1_name = {
|
||||||
let p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
let p1 = pin1
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
||||||
p1.name().to_string()
|
p1.name().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let pin2_name = {
|
let pin2_name = {
|
||||||
let p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
let p2 = pin2
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
||||||
p2.name().to_string()
|
p2.name().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Connect pin1 to pin2
|
// Connect pin1 to pin2
|
||||||
{
|
{
|
||||||
let mut p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
let mut p1 = pin1
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
||||||
p1.connect_to(pin2.clone());
|
p1.connect_to(pin2.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect pin2 to pin1 (bidirectional)
|
// Connect pin2 to pin1 (bidirectional)
|
||||||
{
|
{
|
||||||
let mut p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
let mut p2 = pin2
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
||||||
p2.connect_to(pin1.clone());
|
p2.connect_to(pin1.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update connection graph
|
// Update connection graph
|
||||||
self.connections.entry(pin1_name.clone())
|
self.connections
|
||||||
|
.entry(pin1_name.clone())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(pin2_name.clone());
|
.push(pin2_name.clone());
|
||||||
|
|
||||||
self.connections.entry(pin2_name)
|
self.connections
|
||||||
|
.entry(pin2_name)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(pin1_name);
|
.push(pin1_name);
|
||||||
|
|
||||||
|
|
@ -76,26 +90,38 @@ impl ConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disconnect two pins
|
/// Disconnect two pins
|
||||||
pub fn disconnect_pins(&mut self, pin1: &Arc<Mutex<Pin>>, pin2: &Arc<Mutex<Pin>>) -> Result<(), String> {
|
pub fn disconnect_pins(
|
||||||
|
&mut self,
|
||||||
|
pin1: &Arc<Mutex<Pin>>,
|
||||||
|
pin2: &Arc<Mutex<Pin>>,
|
||||||
|
) -> Result<(), String> {
|
||||||
let pin1_name = {
|
let pin1_name = {
|
||||||
let p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
let p1 = pin1
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
||||||
p1.name().to_string()
|
p1.name().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let pin2_name = {
|
let pin2_name = {
|
||||||
let p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
let p2 = pin2
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
||||||
p2.name().to_string()
|
p2.name().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Disconnect pin1 from pin2
|
// Disconnect pin1 from pin2
|
||||||
{
|
{
|
||||||
let mut p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
let mut p1 = pin1
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
|
||||||
p1.disconnect_from_pin(pin2);
|
p1.disconnect_from_pin(pin2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect pin2 from pin1
|
// Disconnect pin2 from pin1
|
||||||
{
|
{
|
||||||
let mut p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
let mut p2 = pin2
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
|
||||||
p2.disconnect_from_pin(pin1);
|
p2.disconnect_from_pin(pin1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +144,8 @@ impl ConnectionManager {
|
||||||
|
|
||||||
/// Check if two pins are connected
|
/// Check if two pins are connected
|
||||||
pub fn are_connected(&self, pin1_name: &str, pin2_name: &str) -> bool {
|
pub fn are_connected(&self, pin1_name: &str, pin2_name: &str) -> bool {
|
||||||
self.connections.get(pin1_name)
|
self.connections
|
||||||
|
.get(pin1_name)
|
||||||
.map(|connections| connections.contains(&pin2_name.to_string()))
|
.map(|connections| connections.contains(&pin2_name.to_string()))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
@ -126,12 +153,15 @@ impl ConnectionManager {
|
||||||
/// Disconnect all pins from a given pin
|
/// Disconnect all pins from a given pin
|
||||||
pub fn disconnect_all(&mut self, pin: &Arc<Mutex<Pin>>) -> Result<(), String> {
|
pub fn disconnect_all(&mut self, pin: &Arc<Mutex<Pin>>) -> Result<(), String> {
|
||||||
let pin_name = {
|
let pin_name = {
|
||||||
let p = pin.lock().map_err(|e| format!("Failed to lock pin: {}", e))?;
|
let p = pin
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin: {}", e))?;
|
||||||
p.name().to_string()
|
p.name().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get all connected pin names before disconnecting
|
// Get all connected pin names before disconnecting
|
||||||
let connected_names: Vec<String> = self.connections
|
let connected_names: Vec<String> = self
|
||||||
|
.connections
|
||||||
.get(&pin_name)
|
.get(&pin_name)
|
||||||
.map(|v| v.clone())
|
.map(|v| v.clone())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
@ -152,7 +182,9 @@ impl ConnectionManager {
|
||||||
|
|
||||||
// Clear the pin's internal connections
|
// Clear the pin's internal connections
|
||||||
{
|
{
|
||||||
let mut p = pin.lock().map_err(|e| format!("Failed to lock pin: {}", e))?;
|
let mut p = pin
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| format!("Failed to lock pin: {}", e))?;
|
||||||
p.clear_connections();
|
p.clear_connections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
src/lib.rs
14
src/lib.rs
|
|
@ -1,12 +1,12 @@
|
||||||
pub mod component;
|
|
||||||
pub mod pin;
|
|
||||||
pub mod connection;
|
|
||||||
pub mod components;
|
|
||||||
pub mod systems;
|
|
||||||
mod bus;
|
mod bus;
|
||||||
|
pub mod component;
|
||||||
|
pub mod components;
|
||||||
|
pub mod connection;
|
||||||
|
pub mod pin;
|
||||||
|
pub mod systems;
|
||||||
pub mod types;
|
pub 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::{BaseComponent, Component};
|
||||||
pub use pin::{Pin, PinValue};
|
|
||||||
pub use connection::connect_pins;
|
pub use connection::connect_pins;
|
||||||
|
pub use pin::{Pin, PinValue};
|
||||||
|
|
|
||||||
17
src/main.rs
17
src/main.rs
|
|
@ -1,5 +1,5 @@
|
||||||
use rusty_emu::types::U12;
|
|
||||||
use rusty_emu::systems::intel_mcs_4::IntelMcs4;
|
use rusty_emu::systems::intel_mcs_4::IntelMcs4;
|
||||||
|
use rusty_emu::types::U12;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
|
@ -91,7 +91,8 @@ fn run_fibonacci_demo(mcs4: IntelMcs4) {
|
||||||
let mut last_fibonacci = 0;
|
let mut last_fibonacci = 0;
|
||||||
let mut iteration = 0;
|
let mut iteration = 0;
|
||||||
|
|
||||||
while iteration < 20 { // Run for 20 iterations
|
while iteration < 20 {
|
||||||
|
// Run for 20 iterations
|
||||||
thread::sleep(Duration::from_millis(200));
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
if let Ok(system) = mcs4_arc.lock() {
|
if let Ok(system) = mcs4_arc.lock() {
|
||||||
|
|
@ -99,15 +100,21 @@ fn run_fibonacci_demo(mcs4: IntelMcs4) {
|
||||||
let current_fibonacci = state.accumulator;
|
let current_fibonacci = state.accumulator;
|
||||||
|
|
||||||
if current_fibonacci != last_fibonacci {
|
if current_fibonacci != last_fibonacci {
|
||||||
println!("{:5} | {:9} | {:11} | {:04X}",
|
println!(
|
||||||
state.cycle_count, current_fibonacci, state.accumulator, state.program_counter);
|
"{:5} | {:9} | {:11} | {:04X}",
|
||||||
|
state.cycle_count,
|
||||||
|
current_fibonacci,
|
||||||
|
state.accumulator,
|
||||||
|
state.program_counter
|
||||||
|
);
|
||||||
last_fibonacci = current_fibonacci;
|
last_fibonacci = current_fibonacci;
|
||||||
iteration += 1;
|
iteration += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if iteration >= 15 { // Stop after 15 Fibonacci numbers
|
if iteration >= 15 {
|
||||||
|
// Stop after 15 Fibonacci numbers
|
||||||
if let Ok(mut system) = mcs4_arc.lock() {
|
if let Ok(mut system) = mcs4_arc.lock() {
|
||||||
system.stop();
|
system.stop();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/pin.rs
29
src/pin.rs
|
|
@ -92,7 +92,12 @@ impl Pin {
|
||||||
self.set_driver_with_strength(driver_name, value, strength);
|
self.set_driver_with_strength(driver_name, value, strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_driver_with_strength(&mut self, driver_name: Option<String>, value: PinValue, strength: DriveStrength) {
|
pub fn set_driver_with_strength(
|
||||||
|
&mut self,
|
||||||
|
driver_name: Option<String>,
|
||||||
|
value: PinValue,
|
||||||
|
strength: DriveStrength,
|
||||||
|
) {
|
||||||
let driver_id = driver_name.unwrap_or_else(|| "anonymous".to_string());
|
let driver_id = driver_name.unwrap_or_else(|| "anonymous".to_string());
|
||||||
|
|
||||||
if value == PinValue::HighZ && strength == DriveStrength::HighImpedance {
|
if value == PinValue::HighZ && strength == DriveStrength::HighImpedance {
|
||||||
|
|
@ -140,7 +145,11 @@ impl Pin {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect_to(&mut self, other_pin: Arc<Mutex<Pin>>) {
|
pub fn connect_to(&mut self, other_pin: Arc<Mutex<Pin>>) {
|
||||||
if !self.connected_pins.iter().any(|p| Arc::ptr_eq(p, &other_pin)) {
|
if !self
|
||||||
|
.connected_pins
|
||||||
|
.iter()
|
||||||
|
.any(|p| Arc::ptr_eq(p, &other_pin))
|
||||||
|
{
|
||||||
self.connected_pins.push(other_pin);
|
self.connected_pins.push(other_pin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -190,7 +199,9 @@ impl Pin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all drivers with the strongest strength
|
// Get all drivers with the strongest strength
|
||||||
let strong_drivers: Vec<PinValue> = self.drivers.values()
|
let strong_drivers: Vec<PinValue> = self
|
||||||
|
.drivers
|
||||||
|
.values()
|
||||||
.filter(|(_, strength)| *strength == max_strength)
|
.filter(|(_, strength)| *strength == max_strength)
|
||||||
.map(|(value, _)| *value)
|
.map(|(value, _)| *value)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -216,7 +227,9 @@ impl Pin {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_connected_to(&self, other_pin: &Arc<Mutex<Pin>>) -> bool {
|
pub fn is_connected_to(&self, other_pin: &Arc<Mutex<Pin>>) -> bool {
|
||||||
self.connected_pins.iter().any(|p| Arc::ptr_eq(p, other_pin))
|
self.connected_pins
|
||||||
|
.iter()
|
||||||
|
.any(|p| Arc::ptr_eq(p, other_pin))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection_count(&self) -> usize {
|
pub fn get_connection_count(&self) -> usize {
|
||||||
|
|
@ -248,7 +261,9 @@ impl std::fmt::Display for Pin {
|
||||||
if !self.drivers.is_empty() {
|
if !self.drivers.is_empty() {
|
||||||
write!(f, " [drivers: ")?;
|
write!(f, " [drivers: ")?;
|
||||||
for (i, (driver, (value, strength))) in self.drivers.iter().enumerate() {
|
for (i, (driver, (value, strength))) in self.drivers.iter().enumerate() {
|
||||||
if i > 0 { write!(f, ", ")?; }
|
if i > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
write!(f, "{}={}({})", driver, value.to_char(), *strength as u8)?;
|
write!(f, "{}={}({})", driver, value.to_char(), *strength as u8)?;
|
||||||
}
|
}
|
||||||
write!(f, "]")?;
|
write!(f, "]")?;
|
||||||
|
|
@ -301,7 +316,7 @@ mod tests {
|
||||||
pin.set_driver_with_strength(
|
pin.set_driver_with_strength(
|
||||||
Some("weak".to_string()),
|
Some("weak".to_string()),
|
||||||
PinValue::High,
|
PinValue::High,
|
||||||
DriveStrength::Weak
|
DriveStrength::Weak,
|
||||||
);
|
);
|
||||||
assert_eq!(pin.read(), PinValue::High);
|
assert_eq!(pin.read(), PinValue::High);
|
||||||
|
|
||||||
|
|
@ -309,7 +324,7 @@ mod tests {
|
||||||
pin.set_driver_with_strength(
|
pin.set_driver_with_strength(
|
||||||
Some("strong".to_string()),
|
Some("strong".to_string()),
|
||||||
PinValue::Low,
|
PinValue::Low,
|
||||||
DriveStrength::Strong
|
DriveStrength::Strong,
|
||||||
);
|
);
|
||||||
assert_eq!(pin.read(), PinValue::Low);
|
assert_eq!(pin.read(), PinValue::Low);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::component::Component;
|
||||||
use crate::components::clock::generic_clock::GenericClock;
|
use crate::components::clock::generic_clock::GenericClock;
|
||||||
use crate::components::cpu::intel_4004::Intel4004;
|
use crate::components::cpu::intel_4004::Intel4004;
|
||||||
use crate::component::Component;
|
|
||||||
use crate::components::memory::intel_4001::Intel4001;
|
use crate::components::memory::intel_4001::Intel4001;
|
||||||
use crate::components::memory::intel_4002::Intel4002;
|
use crate::components::memory::intel_4002::Intel4002;
|
||||||
use crate::pin::Pin;
|
use crate::pin::Pin;
|
||||||
|
|
@ -29,23 +29,29 @@ impl IntelMcs4 {
|
||||||
fn initialize_system(&mut self) {
|
fn initialize_system(&mut self) {
|
||||||
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,
|
||||||
|
))),
|
||||||
);
|
);
|
||||||
self.components.insert(
|
self.components.insert(
|
||||||
"clock".to_string(),
|
"clock".to_string(),
|
||||||
Arc::new(Mutex::new(GenericClock::new("SYSTEM_CLOCK".to_string(), 750_000.0)))
|
Arc::new(Mutex::new(GenericClock::new(
|
||||||
|
"SYSTEM_CLOCK".to_string(),
|
||||||
|
750_000.0,
|
||||||
|
))),
|
||||||
);
|
);
|
||||||
self.components.insert(
|
self.components.insert(
|
||||||
"ram".to_string(),
|
"ram".to_string(),
|
||||||
Arc::new(Mutex::new(Intel4002::new("RAM_4002".to_string())))
|
Arc::new(Mutex::new(Intel4002::new("RAM_4002".to_string()))),
|
||||||
);
|
);
|
||||||
self.components.insert(
|
self.components.insert(
|
||||||
"rom1".to_string(),
|
"rom1".to_string(),
|
||||||
Arc::new(Mutex::new(Intel4001::new("ROM_4001_1".to_string())))
|
Arc::new(Mutex::new(Intel4001::new("ROM_4001_1".to_string()))),
|
||||||
);
|
);
|
||||||
self.components.insert(
|
self.components.insert(
|
||||||
"rom2".to_string(),
|
"rom2".to_string(),
|
||||||
Arc::new(Mutex::new(Intel4001::new("ROM_4001_2".to_string())))
|
Arc::new(Mutex::new(Intel4001::new("ROM_4001_2".to_string()))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +124,12 @@ impl IntelMcs4 {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_rom_data(&mut self, rom_chip: usize, data: Vec<u8>, offset: usize) -> Result<(), String> {
|
pub fn load_rom_data(
|
||||||
|
&mut self,
|
||||||
|
rom_chip: usize,
|
||||||
|
data: Vec<u8>,
|
||||||
|
offset: usize,
|
||||||
|
) -> Result<(), String> {
|
||||||
let rom_key = match rom_chip {
|
let rom_key = match rom_chip {
|
||||||
1 => "rom1",
|
1 => "rom1",
|
||||||
2 => "rom2",
|
2 => "rom2",
|
||||||
|
|
@ -128,7 +139,12 @@ impl IntelMcs4 {
|
||||||
if let Some(rom_component) = self.components.get(rom_key) {
|
if let Some(rom_component) = self.components.get(rom_key) {
|
||||||
if let Ok(_rom) = rom_component.lock() {
|
if let Ok(_rom) = rom_component.lock() {
|
||||||
// For now, just log the operation
|
// For now, just log the operation
|
||||||
println!("Loaded {} bytes into ROM{} at offset {}", data.len(), rom_chip, offset);
|
println!(
|
||||||
|
"Loaded {} bytes into ROM{} at offset {}",
|
||||||
|
data.len(),
|
||||||
|
rom_chip,
|
||||||
|
offset
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -181,9 +197,13 @@ impl IntelMcs4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection_graph(&self) -> Vec<Vec<String>> {
|
pub fn get_connection_graph(&self) -> Vec<Vec<String>> {
|
||||||
vec![
|
vec![vec![
|
||||||
vec!["cpu".to_string(), "clock".to_string(), "ram".to_string(), "rom1".to_string(), "rom2".to_string()],
|
"cpu".to_string(),
|
||||||
]
|
"clock".to_string(),
|
||||||
|
"ram".to_string(),
|
||||||
|
"rom1".to_string(),
|
||||||
|
"rom2".to_string(),
|
||||||
|
]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,7 +264,6 @@ impl Component for IntelMcs4 {
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
self.stop();
|
self.stop();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue