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
|
|
@ -8,8 +8,8 @@ use crate::pin::{Pin, PinValue};
|
|||
|
||||
pub struct GenericClock {
|
||||
base: BaseComponent,
|
||||
frequency: f64, // Frequency in Hz
|
||||
duty_cycle: f64, // Duty cycle as percentage (0.0 to 1.0)
|
||||
frequency: f64, // Frequency in Hz
|
||||
duty_cycle: f64, // Duty cycle as percentage (0.0 to 1.0)
|
||||
current_state: PinValue,
|
||||
last_transition: Instant,
|
||||
half_period: Duration,
|
||||
|
|
@ -232,8 +232,13 @@ impl GenericClock {
|
|||
let half_period = self.half_period;
|
||||
|
||||
thread::spawn(move || {
|
||||
for i in 0..count * 2 { // Each cycle has two transitions
|
||||
let state = if i % 2 == 0 { PinValue::High } else { PinValue::Low };
|
||||
for i in 0..count * 2 {
|
||||
// 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() {
|
||||
pin_guard.set_driver(Some(clock_name.clone()), state);
|
||||
|
|
@ -313,6 +318,6 @@ mod tests {
|
|||
fn test_clock_burst() {
|
||||
let mut clock = GenericClock::new("BURST_CLK".to_string(), 1_000_000.0);
|
||||
clock.generate_clock_burst(5); // 5 clock cycles
|
||||
// Note: In a real test, you'd want to verify the pin states
|
||||
// Note: In a real test, you'd want to verify the pin states
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
pub(crate) mod generic_clock;
|
||||
pub(crate) mod generic_clock;
|
||||
|
|
|
|||
|
|
@ -137,4 +137,4 @@ impl CMOS65C02Extensions for WDC65C02 {
|
|||
}
|
||||
|
||||
// Implement other 65C02-specific methods...
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
|
|||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use crate::component::{BaseComponent, Component, RunnableComponent};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
use crate::types::U12;
|
||||
|
||||
|
|
@ -44,8 +44,7 @@ pub struct Intel4004 {
|
|||
impl Intel4004 {
|
||||
pub fn new(name: String, clock_speed: f64) -> Self {
|
||||
let pin_names = vec![
|
||||
"D0", "D1", "D2", "D3",
|
||||
"SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2",
|
||||
"D0", "D1", "D2", "D3", "SYNC", "CM_ROM", "CM_RAM", "TEST", "RESET", "PHI1", "PHI2",
|
||||
];
|
||||
|
||||
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 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -207,7 +207,7 @@ impl Intel4004 {
|
|||
_ => {
|
||||
let mut a = 0;
|
||||
let mut b = 1;
|
||||
for _ in 2..=n%32 {
|
||||
for _ in 2..=n % 32 {
|
||||
let next: u32 = a + b;
|
||||
a = b;
|
||||
b = next;
|
||||
|
|
@ -291,6 +291,7 @@ impl Component for Intel4004 {
|
|||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
// Time-slice model: run in a loop calling update() each cycle
|
||||
self.base.set_running(true);
|
||||
self.reset();
|
||||
|
||||
|
|
@ -311,4 +312,8 @@ impl Component for Intel4004 {
|
|||
fn is_running(&self) -> bool {
|
||||
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
|
||||
mod cpu_traits;
|
||||
pub mod intel_4004;
|
||||
pub mod mos_6502;
|
||||
pub mod wdc_65c02;
|
||||
pub mod intel_4004;
|
||||
mod cpu_traits;
|
||||
|
||||
// Re-export the CPU types
|
||||
pub use intel_4004::Intel4004;
|
||||
pub use mos_6502::MOS6502;
|
||||
pub use wdc_65c02::WDC65C02;
|
||||
pub use intel_4004::Intel4004;
|
||||
|
|
@ -26,15 +26,16 @@ pub struct MOS6502 {
|
|||
impl MOS6502 {
|
||||
pub fn new(name: String) -> Self {
|
||||
let pin_names = vec![
|
||||
"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
|
||||
"RW", // Read/Write
|
||||
"IRQ", // Interrupt Request
|
||||
"NMI", // Non-Maskable Interrupt
|
||||
"RES", // Reset
|
||||
"CLK", // Clock
|
||||
"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
|
||||
"RW", // Read/Write
|
||||
"IRQ", // Interrupt Request
|
||||
"NMI", // Non-Maskable Interrupt
|
||||
"RES", // Reset
|
||||
"CLK", // Clock
|
||||
"SYNC", // Sync
|
||||
"RDY", // Ready
|
||||
"RDY", // Ready
|
||||
];
|
||||
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
|
@ -46,7 +47,7 @@ impl MOS6502 {
|
|||
y_register: 0,
|
||||
stack_pointer: 0xFD,
|
||||
program_counter: 0xFFFC, // Reset vector location
|
||||
status_register: 0x20, // Always set bit 5
|
||||
status_register: 0x20, // Always set bit 5
|
||||
cycle_count: 0,
|
||||
is_reset: false,
|
||||
is_running: false,
|
||||
|
|
@ -73,7 +74,11 @@ impl MOS6502 {
|
|||
if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +90,11 @@ impl MOS6502 {
|
|||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -293,4 +302,4 @@ mod tests {
|
|||
|
||||
assert_eq!(cpu.get_program_counter(), 0xFFFC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,4 +168,4 @@ mod tests {
|
|||
assert_eq!(cpu.get_base_cpu().get_accumulator(), 0);
|
||||
assert_eq!(cpu.get_base_cpu().get_x_register(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ impl GenericRam {
|
|||
}
|
||||
|
||||
// Control pins
|
||||
pin_names.push("CS".parse().unwrap()); // Chip Select
|
||||
pin_names.push("WE".parse().unwrap()); // Write Enable
|
||||
pin_names.push("OE".parse().unwrap()); // Output Enable
|
||||
pin_names.push("CS".parse().unwrap()); // Chip Select
|
||||
pin_names.push("WE".parse().unwrap()); // Write Enable
|
||||
pin_names.push("OE".parse().unwrap()); // Output Enable
|
||||
|
||||
let pin_refs: Vec<&str> = pin_names.iter().map(|s| s.as_str()).collect();
|
||||
let pins = BaseComponent::create_pin_map(&pin_refs, &name);
|
||||
|
|
@ -70,7 +70,9 @@ impl GenericRam {
|
|||
if offset + data.len() > self.memory.len() {
|
||||
return Err(format!(
|
||||
"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() {
|
||||
return Err(format!(
|
||||
"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 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,4 +416,4 @@ mod tests {
|
|||
let ram = GenericRam::new("TEST_RAM".to_string(), 256, 8, 8);
|
||||
assert!(!ram.is_running());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,17 +25,29 @@ impl GenericRom {
|
|||
|
||||
// Create address pins (A0, A1, A2, ...)
|
||||
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, ...)
|
||||
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
|
||||
pins.insert("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)))));
|
||||
pins.insert(
|
||||
"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];
|
||||
|
||||
|
|
@ -70,7 +82,11 @@ impl GenericRom {
|
|||
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();
|
||||
|
||||
// Check if file exists
|
||||
|
|
@ -80,14 +96,18 @@ impl GenericRom {
|
|||
|
||||
// Check if offset is within bounds
|
||||
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)
|
||||
.map_err(|e| format!("Failed to open file: {}", e))?;
|
||||
let mut file = File::open(path_ref).map_err(|e| format!("Failed to open file: {}", e))?;
|
||||
|
||||
// Get file size
|
||||
let metadata = file.metadata()
|
||||
let metadata = file
|
||||
.metadata()
|
||||
.map_err(|e| format!("Failed to get file metadata: {}", e))?;
|
||||
let file_size = metadata.len() as usize;
|
||||
|
||||
|
|
@ -95,7 +115,9 @@ impl GenericRom {
|
|||
if offset + file_size > self.memory.len() {
|
||||
return Err(format!(
|
||||
"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() {
|
||||
return Err(format!(
|
||||
"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(())
|
||||
}
|
||||
|
||||
|
||||
fn read_address(&self) -> u32 {
|
||||
let mut address = 0;
|
||||
|
||||
|
|
@ -170,7 +193,10 @@ impl GenericRom {
|
|||
for i in 0..self.data_width {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use tempfile::NamedTempFile;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rom_creation() {
|
||||
|
|
@ -329,4 +362,4 @@ mod tests {
|
|||
assert_eq!(rom.memory[2], 0x56);
|
||||
assert_eq!(rom.memory[3], 0x78);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use crate::component::{BaseComponent, Component, RunnableComponent};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
/// Intel 4001 - 256-byte ROM with integrated I/O
|
||||
|
|
@ -17,12 +16,19 @@ pub struct Intel4001 {
|
|||
output_latch: u8,
|
||||
input_latch: u8,
|
||||
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)]
|
||||
enum IoMode {
|
||||
Input, // I/O pins as inputs
|
||||
Output, // I/O pins as outputs
|
||||
Input, // I/O pins as inputs
|
||||
Output, // I/O pins as outputs
|
||||
Bidirectional, // I/O pins bidirectional
|
||||
}
|
||||
|
||||
|
|
@ -31,18 +37,26 @@ impl Intel4001 {
|
|||
// Intel 4001 has 256 bytes of ROM
|
||||
let rom_size = 256;
|
||||
|
||||
// Intel 4001 pinout:
|
||||
// - 10 address pins (A0-A9, but only A0-A7 used for 256-byte addressing)
|
||||
// Intel 4001 pinout (per datasheet):
|
||||
// - 4 data pins (D0-D3) for multiplexed address/data
|
||||
// - 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![
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"IO0", "IO1", "IO2", "IO3", // I/O pins
|
||||
"SYNC", // Sync signal
|
||||
"CM_ROM", // ROM Chip Select
|
||||
"CM_RAM", // RAM Chip Select (for system configuration)
|
||||
"RESET", // Reset
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"IO0", "IO1", "IO2", "IO3", // I/O pins
|
||||
"SYNC", // Sync signal
|
||||
"CM", // CM-ROM: ROM/RAM Chip Select
|
||||
"CI", // CM-RAM: RAM Chip Select (I/O vs ROM access)
|
||||
"RESET", // Reset
|
||||
"PHI1", // Clock phase 1
|
||||
"PHI2", // Clock phase 2
|
||||
];
|
||||
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
|
@ -57,6 +71,11 @@ impl Intel4001 {
|
|||
output_latch: 0,
|
||||
input_latch: 0,
|
||||
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() {
|
||||
return Err(format!(
|
||||
"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) {
|
||||
// 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 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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) {
|
||||
for i in 0..4 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("IO{}", i)) {
|
||||
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) {
|
||||
// SYNC: Marks the start of an instruction cycle
|
||||
let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
pin_guard.read() == PinValue::High
|
||||
|
|
@ -179,7 +261,8 @@ impl Intel4001 {
|
|||
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() {
|
||||
pin_guard.read() == PinValue::High
|
||||
} else {
|
||||
|
|
@ -189,7 +272,9 @@ impl Intel4001 {
|
|||
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() {
|
||||
pin_guard.read() == PinValue::High
|
||||
} else {
|
||||
|
|
@ -213,12 +298,18 @@ impl Intel4001 {
|
|||
}
|
||||
|
||||
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.input_latch = 0;
|
||||
self.io_mode = IoMode::Input;
|
||||
self.tri_state_data_bus();
|
||||
self.tri_state_io_pins();
|
||||
// Reset access latency state
|
||||
self.address_latched = false;
|
||||
self.address_latch_time = None;
|
||||
self.data_available = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -247,25 +338,41 @@ impl Intel4001 {
|
|||
fn handle_memory_operation(&mut self) -> bool {
|
||||
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
|
||||
|
||||
// Memory operations occur when SYNC is high and CM-ROM is high
|
||||
if sync && cm_rom {
|
||||
if cm_ram {
|
||||
// Second cycle of memory read - output data
|
||||
let address = self.last_address;
|
||||
if (address as usize) < self.memory.len() {
|
||||
let data = self.memory[address as usize];
|
||||
self.write_data_bus(data);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// First cycle of memory operation - latch address
|
||||
let address_low = self.read_data_bus();
|
||||
self.last_address = address_low as u16;
|
||||
// Tri-state during address phase
|
||||
self.tri_state_data_bus();
|
||||
// TRI-STATE RULE: ROM only drives data bus when ALL of these are true:
|
||||
// - SYNC=1 (instruction cycle active)
|
||||
// - CM-ROM=1 (ROM chip selected)
|
||||
// - 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;
|
||||
if (address as usize) < self.memory.len() {
|
||||
let data = self.memory[address as usize];
|
||||
self.write_data_bus(data);
|
||||
return true; // Successfully drove the bus
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
self.last_address = address_low as u16;
|
||||
self.address_latched = true;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -284,11 +391,28 @@ impl Component for Intel4001 {
|
|||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
// Respect access timing
|
||||
if self.last_access.elapsed() < self.access_time {
|
||||
// Only update on PHI1 rising edge (synchronous with CPU clock)
|
||||
if !self.is_phi1_rising_edge() {
|
||||
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();
|
||||
|
||||
// Handle I/O operations (higher priority than memory operations)
|
||||
|
|
@ -297,23 +421,28 @@ impl Component for Intel4001 {
|
|||
// Handle memory operations
|
||||
let memory_accessed = self.handle_memory_operation();
|
||||
|
||||
// If no memory operation occurred, ensure data bus is tri-stated when not active
|
||||
if !memory_accessed {
|
||||
let (sync, cm_rom, _, _) = self.read_control_pins();
|
||||
if !sync || !cm_rom {
|
||||
self.tri_state_data_bus();
|
||||
}
|
||||
// Additional safety: ensure data bus is tri-stated when ROM is not actively driving
|
||||
// This handles any edge cases not covered in handle_memory_operation
|
||||
if !memory_accessed && !self.should_drive_data_bus() {
|
||||
self.tri_state_data_bus();
|
||||
}
|
||||
|
||||
self.last_access = Instant::now();
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
// Time-slice model: run in a loop calling update() each cycle
|
||||
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() {
|
||||
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
|
||||
self.tri_state_data_bus();
|
||||
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 {
|
||||
|
|
@ -330,6 +464,8 @@ impl Component for Intel4001 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RunnableComponent for Intel4001 {}
|
||||
|
||||
// Intel 4001 specific methods
|
||||
impl Intel4001 {
|
||||
pub fn get_rom_size(&self) -> usize {
|
||||
|
|
@ -435,4 +571,4 @@ mod tests {
|
|||
assert_eq!(IoMode::Output.to_string(), "Output");
|
||||
assert_eq!(IoMode::Bidirectional.to_string(), "Bidirectional");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ impl Intel4002 {
|
|||
// - 4 output port pins (O0-O3)
|
||||
// - Control pins: SYNC, CM-ROM, CM-RAM, RESET
|
||||
let pin_names = vec![
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"O0", "O1", "O2", "O3", // Output port pins
|
||||
"SYNC", // Sync signal
|
||||
"CM_ROM", // ROM Chip Select
|
||||
"CM_RAM", // RAM Chip Select
|
||||
"RESET", // Reset
|
||||
"D0", "D1", "D2", "D3", // Data/Address pins
|
||||
"O0", "O1", "O2", "O3", // Output port pins
|
||||
"SYNC", // Sync signal
|
||||
"CM_ROM", // ROM Chip Select
|
||||
"CM_RAM", // RAM Chip Select
|
||||
"RESET", // Reset
|
||||
];
|
||||
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
|
@ -119,7 +119,8 @@ impl Intel4002 {
|
|||
for i in 0..4 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
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();
|
||||
|
||||
// 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 {
|
||||
// Read from RAM and output data
|
||||
|
|
@ -415,7 +417,7 @@ impl Intel4002 {
|
|||
} else if start > 40 {
|
||||
Vec::new()
|
||||
} else {
|
||||
self.memory[start..40].to_vec()
|
||||
self.memory[start..40].to_vec()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -475,7 +477,7 @@ mod tests {
|
|||
let mut ram = Intel4002::new("RAM_4002".to_string());
|
||||
|
||||
// Write test data to different banks
|
||||
ram.write_ram(0, 0x01).unwrap(); // Bank 0
|
||||
ram.write_ram(0, 0x01).unwrap(); // Bank 0
|
||||
ram.write_ram(16, 0x02).unwrap(); // Bank 1
|
||||
ram.write_ram(32, 0x03).unwrap(); // Bank 2
|
||||
|
||||
|
|
@ -507,4 +509,4 @@ mod tests {
|
|||
assert_eq!(ram.read_ram(0).unwrap(), 0);
|
||||
assert_eq!(ram.read_ram(10).unwrap(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
pub mod intel_4002;
|
||||
pub mod intel_4001;
|
||||
mod generic_ram;
|
||||
pub mod generic_rom;
|
||||
mod generic_ram;
|
||||
pub mod intel_4001;
|
||||
pub mod intel_4002;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
pub mod cpu;
|
||||
pub mod clock;
|
||||
pub mod cpu;
|
||||
pub mod memory;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue