New implementation with threads per component
This commit is contained in:
parent
ac8ebbafe9
commit
bfbddda58c
15 changed files with 170 additions and 265 deletions
|
|
@ -5,4 +5,3 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serialport = "4.6.0"
|
serialport = "4.6.0"
|
||||||
connect = "0.2.4"
|
|
||||||
|
|
|
||||||
72
src/component.rs
Normal file
72
src/component.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use crate::pin::{Pin, PinValue};
|
||||||
|
// Component trait
|
||||||
|
pub trait Component: Send {
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
fn pins(&self) -> &HashMap<String, Arc<Pin>>;
|
||||||
|
fn get_pin(&self, name: &str) -> Option<Arc<Pin>>;
|
||||||
|
fn update(&mut self) -> Result<(), String>;
|
||||||
|
fn run(&mut self);
|
||||||
|
fn stop(&mut self);
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BaseComponent {
|
||||||
|
pub name: String,
|
||||||
|
pub pins: HashMap<String, Arc<Pin>>,
|
||||||
|
pub running: Arc<Mutex<bool>>,
|
||||||
|
}
|
||||||
|
impl BaseComponent {
|
||||||
|
pub fn new(name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
pins: HashMap::new(),
|
||||||
|
running: Arc::new(Mutex::new(false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_pin(&mut self, name: String, initial_value: PinValue, initial_output_enable: bool) -> Arc<Pin> {
|
||||||
|
let pin = Arc::new(Pin {
|
||||||
|
name: name.clone(),
|
||||||
|
value: Arc::new(RwLock::new(initial_value)),
|
||||||
|
output_enable: Arc::new(Mutex::new(initial_output_enable)),
|
||||||
|
connections: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.pins.insert(name, pin.clone());
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Component for BaseComponent {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||||
|
&self.pins
|
||||||
|
}
|
||||||
|
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||||
|
self.pins.get(name).cloned()
|
||||||
|
}
|
||||||
|
fn update(&mut self) -> Result<(), String> {
|
||||||
|
// Base implementation does nothing
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn run(&mut self) {
|
||||||
|
*self.running.lock().unwrap() = true;
|
||||||
|
let running = self.running.clone();
|
||||||
|
let mut self_clone = self.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
while *running.lock().unwrap() {
|
||||||
|
if let Err(e) = self_clone.update() {
|
||||||
|
eprintln!("Error in {}: {}", self_clone.name, e);
|
||||||
|
}
|
||||||
|
thread::sleep(Duration::from_micros(10));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn stop(&mut self) {
|
||||||
|
*self.running.lock().unwrap() = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::{PinValue, BaseComponent, Component, Pin};
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use crate::component::{BaseComponent, Component};
|
||||||
|
use crate::pin::{Pin, PinValue};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GenericClock {
|
pub struct GenericClock {
|
||||||
base: BaseComponent,
|
base: BaseComponent,
|
||||||
|
|
@ -13,7 +15,7 @@ pub struct GenericClock {
|
||||||
impl GenericClock {
|
impl GenericClock {
|
||||||
pub fn new(name: String, frequency_hz: u64) -> Self {
|
pub fn new(name: String, frequency_hz: u64) -> Self {
|
||||||
let mut base = BaseComponent::new(name);
|
let mut base = BaseComponent::new(name);
|
||||||
let out_pin = base.add_pin("out".to_string(), PinValue::Low, true);
|
let _out_pin = base.add_pin("out".to_string(), PinValue::Low, true);
|
||||||
|
|
||||||
let half_period_ms = 500 / frequency_hz; // Convert to milliseconds
|
let half_period_ms = 500 / frequency_hz; // Convert to milliseconds
|
||||||
|
|
||||||
|
|
@ -24,29 +26,20 @@ impl GenericClock {
|
||||||
last_state: false,
|
last_state: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn out(&self) -> Option<Arc<Pin>> {
|
pub fn out(&self) -> Option<Arc<Pin>> {
|
||||||
self.base.get_pin("out")
|
self.base.get_pin("out")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for GenericClock {
|
impl Component for GenericClock {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||||
self.base.get_pin(name)
|
self.base.get_pin(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
|
|
||||||
self.base.connect_pin(pin_name, other_pin)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self) -> Result<(), String> {
|
fn update(&mut self) -> Result<(), String> {
|
||||||
let out_pin = self.base.pins.get("out").unwrap();
|
let out_pin = self.base.pins.get("out").unwrap();
|
||||||
|
|
||||||
|
|
@ -59,11 +52,9 @@ impl Component for GenericClock {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
self.base.run();
|
self.base.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
self.base.stop();
|
self.base.stop();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{PinValue, Component, Pin};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::component::Component;
|
||||||
|
use crate::pin::Pin;
|
||||||
use crate::components::cpu::mos_6502::MOS6502;
|
use crate::components::cpu::mos_6502::MOS6502;
|
||||||
|
|
||||||
pub struct CMOS65C02 {
|
pub struct CMOS65C02 {
|
||||||
|
|
@ -76,25 +77,17 @@ impl CMOS65C02 {
|
||||||
self.waiting
|
self.waiting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Component by delegating to the base 6502
|
// Implement Component by delegating to the base 6502
|
||||||
impl Component for CMOS65C02 {
|
impl Component for CMOS65C02 {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
self.base_6502.name()
|
self.base_6502.name()
|
||||||
}
|
}
|
||||||
|
fn pins(&self) -> &std::collections::HashMap<String, Arc<Pin>> {
|
||||||
fn pins(&self) -> &std::collections::HashMap<String, Arc<crate::Pin>> {
|
|
||||||
self.base_6502.pins()
|
self.base_6502.pins()
|
||||||
}
|
}
|
||||||
|
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||||
fn get_pin(&self, name: &str) -> Option<Arc<crate::Pin>> {
|
|
||||||
self.base_6502.get_pin(name)
|
self.base_6502.get_pin(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<crate::Pin>) -> Result<(), String> {
|
|
||||||
self.base_6502.connect_pin(pin_name, other_pin)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self) -> Result<(), String> {
|
fn update(&mut self) -> Result<(), String> {
|
||||||
if self.stopped {
|
if self.stopped {
|
||||||
// Don't execute instructions if stopped
|
// Don't execute instructions if stopped
|
||||||
|
|
@ -104,11 +97,9 @@ impl Component for CMOS65C02 {
|
||||||
// Delegate to base implementation
|
// Delegate to base implementation
|
||||||
self.base_6502.update()
|
self.base_6502.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
self.base_6502.run()
|
self.base_6502.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
self.base_6502.stop()
|
self.base_6502.stop()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{PinValue, Component};
|
use crate::component::Component;
|
||||||
use crate::components::cpu::cmos_65c02::CMOS65C02;
|
use crate::components::cpu::cmos_65c02::CMOS65C02;
|
||||||
use crate::components::cpu::mos_6502::{Registers, MOS6502};
|
use crate::components::cpu::mos_6502::{Registers, MOS6502};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,23 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::{PinValue, BaseComponent, Component, Pin};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::component::{BaseComponent, Component};
|
||||||
|
use crate::pin::{Pin, PinValue};
|
||||||
|
|
||||||
// Simple 4-bit and 12-bit types for the 4004
|
// Simple 4-bit and 12-bit types for the 4004
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct u4(u8);
|
struct u4(u8);
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct u12(u16);
|
struct u12(u16);
|
||||||
|
|
||||||
impl u4 {
|
impl u4 {
|
||||||
fn wrapping_add(self, other: u4) -> u4 {
|
fn wrapping_add(self, other: u4) -> u4 {
|
||||||
u4((self.0 + other.0) & 0xF)
|
u4((self.0 + other.0) & 0xF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl u12 {
|
impl u12 {
|
||||||
fn wrapping_add(self, other: u12) -> u12 {
|
fn wrapping_add(self, other: u12) -> u12 {
|
||||||
u12((self.0 + other.0) & 0xFFF)
|
u12((self.0 + other.0) & 0xFFF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Intel4004 {
|
pub struct Intel4004 {
|
||||||
base: BaseComponent,
|
base: BaseComponent,
|
||||||
|
|
@ -27,7 +25,6 @@ pub struct Intel4004 {
|
||||||
accumulator: u4,
|
accumulator: u4,
|
||||||
last_clock_state: PinValue,
|
last_clock_state: PinValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Intel4004 {
|
impl Intel4004 {
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
let mut base = BaseComponent::new(name);
|
let mut base = BaseComponent::new(name);
|
||||||
|
|
@ -44,15 +41,12 @@ impl Intel4004 {
|
||||||
last_clock_state: PinValue::Low,
|
last_clock_state: PinValue::Low,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clk(&self) -> Option<Arc<Pin>> {
|
pub fn clk(&self) -> Option<Arc<Pin>> {
|
||||||
self.base.get_pin("clk")
|
self.base.get_pin("clk")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&self) -> Option<Arc<Pin>> {
|
pub fn reset(&self) -> Option<Arc<Pin>> {
|
||||||
self.base.get_pin("reset")
|
self.base.get_pin("reset")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> Option<Arc<Pin>> {
|
pub fn data(&self) -> Option<Arc<Pin>> {
|
||||||
self.base.get_pin("data")
|
self.base.get_pin("data")
|
||||||
}
|
}
|
||||||
|
|
@ -62,19 +56,12 @@ impl Component for Intel4004 {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||||
self.base.pins()
|
self.base.pins()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||||
self.base.get_pin(name)
|
self.base.get_pin(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
|
|
||||||
self.base.connect_pin(pin_name, other_pin)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self) -> Result<(), String> {
|
fn update(&mut self) -> Result<(), String> {
|
||||||
// Read clock pin (check if any connected component is driving it low)
|
// Read clock pin (check if any connected component is driving it low)
|
||||||
let clk_pin = self.base.pins.get("clk").unwrap();
|
let clk_pin = self.base.pins.get("clk").unwrap();
|
||||||
|
|
@ -103,11 +90,9 @@ impl Component for Intel4004 {
|
||||||
self.last_clock_state = clk_value;
|
self.last_clock_state = clk_value;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
self.base.run();
|
self.base.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
self.base.stop();
|
self.base.stop();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
mod cpu_traits;
|
pub mod cpu_traits;
|
||||||
pub(crate) mod intel_4004;
|
pub mod intel_4004;
|
||||||
mod mos_6502;
|
mod mos_6502;
|
||||||
mod cmos_65c02;
|
mod cmos_65c02;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::{PinValue, BaseComponent, Component, Pin};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::component::{BaseComponent, Component};
|
||||||
|
use crate::pin::{Pin, PinValue};
|
||||||
|
|
||||||
// 6502 addressing modes
|
// 6502 addressing modes
|
||||||
pub enum AddressingMode {
|
pub enum AddressingMode {
|
||||||
|
|
@ -18,7 +19,6 @@ pub enum AddressingMode {
|
||||||
Accumulator,
|
Accumulator,
|
||||||
Relative,
|
Relative,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6502 registers
|
// 6502 registers
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Registers {
|
pub struct Registers {
|
||||||
|
|
@ -29,7 +29,6 @@ pub struct Registers {
|
||||||
pub pc: u16, // Program counter
|
pub pc: u16, // Program counter
|
||||||
pub sr: u8, // Status register
|
pub sr: u8, // Status register
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bit positions in status register
|
// Bit positions in status register
|
||||||
pub const CARRY_FLAG: u8 = 1 << 0;
|
pub const CARRY_FLAG: u8 = 1 << 0;
|
||||||
pub const ZERO_FLAG: u8 = 1 << 1;
|
pub const ZERO_FLAG: u8 = 1 << 1;
|
||||||
|
|
@ -39,7 +38,6 @@ pub const BREAK_COMMAND: u8 = 1 << 4;
|
||||||
pub const UNUSED_FLAG: u8 = 1 << 5;
|
pub const UNUSED_FLAG: u8 = 1 << 5;
|
||||||
pub const OVERFLOW_FLAG: u8 = 1 << 6;
|
pub const OVERFLOW_FLAG: u8 = 1 << 6;
|
||||||
pub const NEGATIVE_FLAG: u8 = 1 << 7;
|
pub const NEGATIVE_FLAG: u8 = 1 << 7;
|
||||||
|
|
||||||
pub struct MOS6502 {
|
pub struct MOS6502 {
|
||||||
base: BaseComponent,
|
base: BaseComponent,
|
||||||
pub(crate) registers: Registers,
|
pub(crate) registers: Registers,
|
||||||
|
|
@ -47,7 +45,6 @@ pub struct MOS6502 {
|
||||||
last_clock_state: PinValue,
|
last_clock_state: PinValue,
|
||||||
// Memory interface would be added here
|
// Memory interface would be added here
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MOS6502 {
|
impl MOS6502 {
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
let mut base = BaseComponent::new(name);
|
let mut base = BaseComponent::new(name);
|
||||||
|
|
@ -68,7 +65,6 @@ impl MOS6502 {
|
||||||
last_clock_state: PinValue::Low,
|
last_clock_state: PinValue::Low,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common 6502 methods that will be inherited
|
// Common 6502 methods that will be inherited
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.registers.pc = 0xFFFC;
|
self.registers.pc = 0xFFFC;
|
||||||
|
|
@ -76,22 +72,18 @@ impl MOS6502 {
|
||||||
self.registers.sr = 0x34; // I flag set, others cleared
|
self.registers.sr = 0x34; // I flag set, others cleared
|
||||||
self.cycle_count = 0;
|
self.cycle_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register_a(&self) -> u8 {
|
pub fn get_register_a(&self) -> u8 {
|
||||||
self.registers.a
|
self.registers.a
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_register_a(&mut self, value: u8) {
|
pub fn set_register_a(&mut self, value: u8) {
|
||||||
self.registers.a = value;
|
self.registers.a = value;
|
||||||
self.update_zero_negative_flags(value);
|
self.update_zero_negative_flags(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common flag handling
|
// Common flag handling
|
||||||
pub fn update_zero_negative_flags(&mut self, value: u8) {
|
pub fn update_zero_negative_flags(&mut self, value: u8) {
|
||||||
self.set_flag(ZERO_FLAG, value == 0);
|
self.set_flag(ZERO_FLAG, value == 0);
|
||||||
self.set_flag(NEGATIVE_FLAG, value & 0x80 != 0);
|
self.set_flag(NEGATIVE_FLAG, value & 0x80 != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_flag(&mut self, flag: u8, condition: bool) {
|
pub fn set_flag(&mut self, flag: u8, condition: bool) {
|
||||||
if condition {
|
if condition {
|
||||||
self.registers.sr |= flag;
|
self.registers.sr |= flag;
|
||||||
|
|
@ -113,7 +105,6 @@ impl MOS6502 {
|
||||||
self.registers.x = self.registers.a;
|
self.registers.x = self.registers.a;
|
||||||
self.update_zero_negative_flags(self.registers.x);
|
self.update_zero_negative_flags(self.registers.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// More common instructions...
|
// More common instructions...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,42 +112,30 @@ impl Component for MOS6502 {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement Component trait methods...
|
// Implement Component trait methods...
|
||||||
fn update(&mut self) -> Result<(), String> {
|
fn update(&mut self) -> Result<(), String> {
|
||||||
// Clock handling and instruction execution
|
// Clock handling and instruction execution
|
||||||
let clk_pin = self.base.pins.get("clk").unwrap();
|
let clk_pin = self.base.pins.get("clk").unwrap();
|
||||||
let clk_value = clk_pin.read();
|
let clk_value = clk_pin.read();
|
||||||
|
|
||||||
// Detect rising edge
|
// Detect rising edge
|
||||||
if clk_value == PinValue::High && self.last_clock_state == PinValue::Low {
|
if clk_value == PinValue::High && self.last_clock_state == PinValue::Low {
|
||||||
self.cycle_count += 1;
|
self.cycle_count += 1;
|
||||||
// Instruction execution would happen here
|
// Instruction execution would happen here
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_clock_state = clk_value;
|
self.last_clock_state = clk_value;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other trait methods...
|
// Other trait methods...
|
||||||
}
|
}
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
pub(crate) mod cpu;
|
pub mod cpu;
|
||||||
pub(crate) mod clock;
|
pub mod clock;
|
||||||
|
|
|
||||||
12
src/connection.rs
Normal file
12
src/connection.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::pin::Pin;
|
||||||
|
|
||||||
|
pub fn connect_pins(source: &Arc<Pin>, destination: &Arc<Pin>) -> Result<(), String> {
|
||||||
|
let mut source_connections = source.connections.lock().unwrap();
|
||||||
|
source_connections.push(destination.clone());
|
||||||
|
|
||||||
|
let mut dest_connections = destination.connections.lock().unwrap();
|
||||||
|
dest_connections.push(source.clone());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
196
src/lib.rs
196
src/lib.rs
|
|
@ -1,188 +1,10 @@
|
||||||
mod systems;
|
pub mod component;
|
||||||
mod components;
|
pub mod pin;
|
||||||
|
pub mod connection;
|
||||||
|
pub mod components;
|
||||||
|
pub mod systems;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
// Re-export commonly used items for easier importing
|
||||||
use std::thread;
|
pub use component::{Component, BaseComponent};
|
||||||
use std::collections::HashMap;
|
pub use pin::{Pin, PinValue};
|
||||||
use std::time::{Duration, Instant};
|
pub use connection::connect_pins;
|
||||||
|
|
||||||
// Pin value representation
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum PinValue {
|
|
||||||
High,
|
|
||||||
Low,
|
|
||||||
HighZ, // High impedance (disconnected)
|
|
||||||
}
|
|
||||||
// Pin structure with output enable
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Pin {
|
|
||||||
pub name: String,
|
|
||||||
pub value: Arc<RwLock<PinValue>>, // Use RwLock for multiple readers
|
|
||||||
pub output_enable: Arc<Mutex<bool>>, // Whether this pin is driving the line
|
|
||||||
pub connections: Arc<Mutex<Vec<Arc<Pin>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component trait
|
|
||||||
impl Pin {
|
|
||||||
// Get the effective value considering all connected pins
|
|
||||||
pub fn read(&self) -> PinValue {
|
|
||||||
let connections = self.connections.lock().unwrap();
|
|
||||||
|
|
||||||
// Check if any connected pin is actively driving low
|
|
||||||
for connected_pin in connections.iter() {
|
|
||||||
let output_enable = connected_pin.output_enable.lock().unwrap();
|
|
||||||
let value = connected_pin.value.read().unwrap();
|
|
||||||
|
|
||||||
if *output_enable && *value == PinValue::Low {
|
|
||||||
return PinValue::Low;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no one is driving low, check if anyone is driving high
|
|
||||||
for connected_pin in connections.iter() {
|
|
||||||
let output_enable = connected_pin.output_enable.lock().unwrap();
|
|
||||||
let value = connected_pin.value.read().unwrap();
|
|
||||||
|
|
||||||
if *output_enable && *value == PinValue::High {
|
|
||||||
return PinValue::High;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no one is driving, return HighZ
|
|
||||||
PinValue::HighZ
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set this pin's value and output enable
|
|
||||||
pub fn write(&self, value: PinValue, output_enable: bool) {
|
|
||||||
*self.value.write().unwrap() = value;
|
|
||||||
*self.output_enable.lock().unwrap() = output_enable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component trait
|
|
||||||
pub trait Component: Send {
|
|
||||||
fn name(&self) -> &str;
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Pin>>;
|
|
||||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>>;
|
|
||||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String>;
|
|
||||||
fn update(&mut self) -> Result<(), String>;
|
|
||||||
fn run(&mut self);
|
|
||||||
fn stop(&mut self);
|
|
||||||
}
|
|
||||||
pub fn connect_pins(source: &Arc<Pin>, destination: &Arc<Pin>) -> Result<(), String> {
|
|
||||||
let mut source_connections = source.connections.lock().unwrap();
|
|
||||||
source_connections.push(destination.clone());
|
|
||||||
|
|
||||||
let mut dest_connections = destination.connections.lock().unwrap();
|
|
||||||
dest_connections.push(source.clone());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
// Helper macro for easy connections
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! connect_pin {
|
|
||||||
($source:expr, $dest:expr) => {
|
|
||||||
crate::connect_pins($source, $dest).unwrap()
|
|
||||||
};
|
|
||||||
}// Base component implementation
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BaseComponent {
|
|
||||||
pub name: String,
|
|
||||||
pub pins: HashMap<String, Arc<Pin>>,
|
|
||||||
pub running: Arc<Mutex<bool>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BaseComponent {
|
|
||||||
pub fn new(name: String) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
pins: HashMap::new(),
|
|
||||||
running: Arc::new(Mutex::new(false)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_pin(&mut self, name: String, initial_value: PinValue, initial_output_enable: bool) -> Arc<Pin> {
|
|
||||||
let pin = Arc::new(Pin {
|
|
||||||
name: name.clone(),
|
|
||||||
value: Arc::new(RwLock::new(initial_value)),
|
|
||||||
output_enable: Arc::new(Mutex::new(initial_output_enable)),
|
|
||||||
connections: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.pins.insert(name, pin.clone());
|
|
||||||
pin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for BaseComponent {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
|
||||||
&self.pins
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
|
||||||
self.pins.get(name).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
|
|
||||||
if let Some(pin) = self.pins.get_mut(pin_name) {
|
|
||||||
let mut connections = pin.connections.lock().unwrap();
|
|
||||||
connections.push(other_pin);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(format!("Pin {} not found", pin_name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self) -> Result<(), String> {
|
|
||||||
// Base implementation does nothing
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&mut self) {
|
|
||||||
*self.running.lock().unwrap() = true;
|
|
||||||
let running = self.running.clone();
|
|
||||||
let mut self_clone = self.clone();
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
while *running.lock().unwrap() {
|
|
||||||
if let Err(e) = self_clone.update() {
|
|
||||||
eprintln!("Error in {}: {}", self_clone.name, e);
|
|
||||||
}
|
|
||||||
thread::sleep(Duration::from_micros(10));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop(&mut self) {
|
|
||||||
*self.running.lock().unwrap() = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = 4;
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn list_ports() {
|
|
||||||
let ports = serialport::available_ports().expect("No ports found!");
|
|
||||||
for port in ports {
|
|
||||||
let mut port = serialport::new(port.port_name, 115_200)
|
|
||||||
.timeout(std::time::Duration::from_millis(10))
|
|
||||||
.open().expect("Failed to open port");
|
|
||||||
|
|
||||||
let output = "This is a test. This is only a test.".as_bytes();
|
|
||||||
let result = port.write(output);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
mod systems;
|
use rusty_emu::systems;
|
||||||
fn main() {
|
fn main() {
|
||||||
|
systems::intel_mcs_4::emulation();
|
||||||
}
|
}
|
||||||
53
src/pin.rs
Normal file
53
src/pin.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
|
// Pin value representation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum PinValue {
|
||||||
|
High,
|
||||||
|
Low,
|
||||||
|
HighZ, // High impedance (disconnected)
|
||||||
|
}
|
||||||
|
// Pin structure with output enable
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Pin {
|
||||||
|
pub name: String,
|
||||||
|
pub value: Arc<RwLock<PinValue>>, // Use RwLock for multiple readers
|
||||||
|
pub output_enable: Arc<Mutex<bool>>, // Whether this pin is driving the line
|
||||||
|
pub connections: Arc<Mutex<Vec<Arc<Pin>>>>,
|
||||||
|
}
|
||||||
|
// Component trait
|
||||||
|
impl Pin {
|
||||||
|
// Get the effective value considering all connected pins
|
||||||
|
pub fn read(&self) -> PinValue {
|
||||||
|
let connections = self.connections.lock().unwrap();
|
||||||
|
|
||||||
|
// Check if any connected pin is actively driving low
|
||||||
|
for connected_pin in connections.iter() {
|
||||||
|
let output_enable = connected_pin.output_enable.lock().unwrap();
|
||||||
|
let value = connected_pin.value.read().unwrap();
|
||||||
|
|
||||||
|
if *output_enable && *value == PinValue::Low {
|
||||||
|
return PinValue::Low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no one is driving low, check if anyone is driving high
|
||||||
|
for connected_pin in connections.iter() {
|
||||||
|
let output_enable = connected_pin.output_enable.lock().unwrap();
|
||||||
|
let value = connected_pin.value.read().unwrap();
|
||||||
|
|
||||||
|
if *output_enable && *value == PinValue::High {
|
||||||
|
return PinValue::High;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no one is driving, return HighZ
|
||||||
|
PinValue::HighZ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set this pin's value and output enable
|
||||||
|
pub fn write(&self, value: PinValue, output_enable: bool) {
|
||||||
|
*self.value.write().unwrap() = value;
|
||||||
|
*self.output_enable.lock().unwrap() = output_enable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use crate::Component;
|
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::connection::connect_pins;
|
||||||
|
|
||||||
fn emulation() {
|
pub fn emulation() {
|
||||||
println!("Starting MCS-4 emulation with pin state resolution...");
|
println!("Starting MCS-4 emulation with pin state resolution...");
|
||||||
|
|
||||||
// Create components
|
// Create components
|
||||||
|
|
@ -13,7 +14,7 @@ fn emulation() {
|
||||||
|
|
||||||
// Connect clock to CPU using the connection macro
|
// Connect clock to CPU using the connection macro
|
||||||
if let (Some(clock_out), Some(cpu_clk)) = (clock.out(), cpu.clk()) {
|
if let (Some(clock_out), Some(cpu_clk)) = (clock.out(), cpu.clk()) {
|
||||||
connect_pin!(&clock_out, &cpu_clk);
|
connect_pins(&clock_out, &cpu_clk).expect("Connection could not be made!");
|
||||||
println!("Connected clock to CPU");
|
println!("Connected clock to CPU");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
mod intel_mcs_4;
|
pub mod intel_mcs_4;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue