Separated two-phase clock from generic clock
This commit is contained in:
parent
7d06837608
commit
c8552a1488
11 changed files with 565 additions and 77 deletions
|
|
@ -173,6 +173,7 @@ impl Component for GenericClock {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Advanced clock features
|
||||
impl GenericClock {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
pub(crate) mod generic_clock;
|
||||
pub mod generic_clock;
|
||||
pub mod two_phase_clock;
|
||||
|
|
|
|||
174
src/components/clock/two_phase_clock.rs
Normal file
174
src/components/clock/two_phase_clock.rs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
pub struct TwoPhaseClock {
|
||||
base: BaseComponent,
|
||||
phi1_state: PinValue,
|
||||
phi2_state: PinValue,
|
||||
last_transition: Instant,
|
||||
phase_time: Duration,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl TwoPhaseClock {
|
||||
pub fn new(name: String, frequency: f64) -> Self {
|
||||
let pin_names = vec!["CLK", "PHI1", "PHI2", "ENABLE"]; // Keep CLK for compatibility
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
||||
let phase_time = if frequency > 0.0 {
|
||||
Duration::from_secs_f64(1.0 / frequency / 2.0) // Half period for each phase
|
||||
} else {
|
||||
Duration::from_secs(1)
|
||||
};
|
||||
|
||||
TwoPhaseClock {
|
||||
base: BaseComponent::new(name, pins),
|
||||
phi1_state: PinValue::High, // Start with PHI1 high
|
||||
phi2_state: PinValue::Low,
|
||||
last_transition: Instant::now(),
|
||||
phase_time,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable(&mut self) {
|
||||
self.enabled = true;
|
||||
self.phi1_state = PinValue::High;
|
||||
self.phi2_state = PinValue::Low;
|
||||
self.last_transition = Instant::now();
|
||||
self.update_outputs();
|
||||
|
||||
// Force an immediate update to ensure outputs are driven
|
||||
self.update_outputs();
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) {
|
||||
self.enabled = false;
|
||||
self.phi1_state = PinValue::Low;
|
||||
self.phi2_state = PinValue::Low;
|
||||
self.update_outputs();
|
||||
}
|
||||
|
||||
fn update_outputs(&self) {
|
||||
// Set CLK output (for compatibility)
|
||||
if let Ok(clk_pin) = self.base.get_pin("CLK") {
|
||||
if let Ok(mut pin_guard) = clk_pin.lock() {
|
||||
// CLK follows PHI1 for compatibility
|
||||
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
|
||||
}
|
||||
}
|
||||
|
||||
// Set PHI1 output
|
||||
if let Ok(phi1_pin) = self.base.get_pin("PHI1") {
|
||||
if let Ok(mut pin_guard) = phi1_pin.lock() {
|
||||
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
|
||||
|
||||
// Check if pin has connections and trigger propagation
|
||||
let connection_count = pin_guard.get_connected_pins().len();
|
||||
if connection_count > 0 {
|
||||
pin_guard.propagate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set PHI2 output
|
||||
if let Ok(phi2_pin) = self.base.get_pin("PHI2") {
|
||||
if let Ok(mut pin_guard) = phi2_pin.lock() {
|
||||
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi2_state);
|
||||
|
||||
// Check if pin has connections and trigger propagation
|
||||
let connection_count = pin_guard.get_connected_pins().len();
|
||||
if connection_count > 0 {
|
||||
pin_guard.propagate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_transition(&self) -> bool {
|
||||
self.last_transition.elapsed() >= self.phase_time
|
||||
}
|
||||
|
||||
fn perform_transition(&mut self) {
|
||||
match (self.phi1_state, self.phi2_state) {
|
||||
(PinValue::High, PinValue::Low) => {
|
||||
// PHI1 -> Low, PHI2 -> High
|
||||
self.phi1_state = PinValue::Low;
|
||||
self.phi2_state = PinValue::High;
|
||||
}
|
||||
(PinValue::Low, PinValue::High) => {
|
||||
// PHI2 -> Low, PHI1 -> High
|
||||
self.phi1_state = PinValue::High;
|
||||
self.phi2_state = PinValue::Low;
|
||||
}
|
||||
_ => {
|
||||
// Reset to known state
|
||||
self.phi1_state = PinValue::High;
|
||||
self.phi2_state = PinValue::Low;
|
||||
}
|
||||
}
|
||||
|
||||
self.update_outputs();
|
||||
self.last_transition = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for TwoPhaseClock {
|
||||
fn name(&self) -> String {
|
||||
self.base.name()
|
||||
}
|
||||
|
||||
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||
self.base.pins()
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||
self.base.get_pin(name)
|
||||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
if !self.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always update outputs to ensure they're driven
|
||||
self.update_outputs();
|
||||
|
||||
if self.should_transition() {
|
||||
self.perform_transition();
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
self.base.set_running(true);
|
||||
self.enable();
|
||||
|
||||
while self.is_running() {
|
||||
self.update();
|
||||
|
||||
// Sleep for a very short time to allow frequent updates
|
||||
thread::sleep(Duration::from_micros(100)); // 100µs = 10kHz update rate
|
||||
|
||||
// Check if should stop
|
||||
if !self.is_running() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.disable();
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
self.base.set_running(false);
|
||||
self.disable();
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
}
|
||||
|
|
@ -569,6 +569,11 @@ impl Intel4004 {
|
|||
// All conditions met: drive data on bus
|
||||
let data = self.data_latch;
|
||||
self.write_data_bus(data);
|
||||
|
||||
if self.cycle_count % 1000 == 0 { // Log every 1000 cycles
|
||||
println!("DEBUG: [{}] CPU State | PC: 0x{:03X} | Cycles: {} | ACC: 0x{:X} | SYNC: {} | CM_ROM: {} | CM_RAM: {} | RAM_Ready: {}",
|
||||
self.base.name(), self.program_counter.value(), self.cycle_count, self.accumulator, sync, cm_rom, cm_ram, self.full_address_ready);
|
||||
}
|
||||
} else {
|
||||
// Conditions not met, tri-state
|
||||
self.tri_state_data_bus();
|
||||
|
|
@ -948,6 +953,8 @@ impl Intel4004 {
|
|||
// I/O and RAM Instructions
|
||||
Instruction::Wrm => {
|
||||
// Write accumulator to RAM - handled by memory interface
|
||||
println!("DEBUG: [CPU] Executing WRM instruction | ACC: 0x{:X} | PC: 0x{:03X}",
|
||||
self.accumulator, self.program_counter.value());
|
||||
self.program_counter.inc();
|
||||
}
|
||||
|
||||
|
|
@ -1110,6 +1117,128 @@ impl Intel4004 {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Test helper: Execute a single instruction for testing
|
||||
/// This bypasses the normal clock synchronization for testing purposes
|
||||
pub fn execute_single_instruction(&mut self) {
|
||||
// Force instruction phase to execute if we're in fetch phase
|
||||
if self.instruction_phase == InstructionPhase::Fetch {
|
||||
self.instruction_phase = InstructionPhase::Execute;
|
||||
}
|
||||
|
||||
if self.instruction_phase == InstructionPhase::Execute {
|
||||
let old_pc = self.program_counter.value();
|
||||
self.execute_instruction();
|
||||
let new_pc = self.program_counter.value();
|
||||
|
||||
println!("DEBUG: [TEST] Single Execute | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X}",
|
||||
old_pc, new_pc, self.accumulator);
|
||||
}
|
||||
}
|
||||
|
||||
/// Test helper: Load a test program into the CPU
|
||||
/// This simulates having a program in ROM for testing
|
||||
pub fn load_test_program(&mut self, program: Vec<u8>) {
|
||||
// Set PC to start of program
|
||||
self.program_counter = U12::new(0);
|
||||
|
||||
// Load program data into index register 0 for testing
|
||||
// In a real implementation, this would load into ROM
|
||||
for (i, &byte) in program.iter().enumerate() {
|
||||
if i < 16 {
|
||||
self.index_registers[i] = byte & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
println!("DEBUG: [{}] Loaded test program: {:02X?}", self.base.name(), program);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_4004_basic_execution() {
|
||||
let mut cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
|
||||
|
||||
// Test basic instruction execution
|
||||
cpu.reset();
|
||||
assert_eq!(cpu.get_accumulator(), 0);
|
||||
assert_eq!(cpu.get_program_counter(), 0);
|
||||
|
||||
// Test individual instructions directly
|
||||
// Test CLB (0xF0) - Clear Both (accumulator and carry)
|
||||
cpu.current_op = Instruction::Clb;
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 0);
|
||||
assert_eq!(cpu.get_carry(), false);
|
||||
|
||||
// Test CLC (0xF1) - Clear Carry
|
||||
cpu.current_op = Instruction::Clc;
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_carry(), false);
|
||||
|
||||
// Test IAC (0xF2) - Increment Accumulator
|
||||
cpu.current_op = Instruction::Iac;
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 1);
|
||||
assert_eq!(cpu.get_carry(), false); // Should not set carry for 0+1
|
||||
|
||||
println!("DEBUG: Basic execution test completed successfully");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_4004_arithmetic_operations() {
|
||||
let mut cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
|
||||
|
||||
cpu.reset();
|
||||
|
||||
// Test LDM (load immediate) - 0xD0 = LDM 0
|
||||
cpu.current_op = Instruction::Ldm(0);
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 0);
|
||||
|
||||
// Test LDM (load immediate) - 0xD5 = LDM 5
|
||||
cpu.current_op = Instruction::Ldm(5);
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 5);
|
||||
|
||||
// Set up register 5 with value 3 for ADD test
|
||||
cpu.set_register(5, 3).unwrap();
|
||||
|
||||
// Test ADD - 0x25 = ADD 5 (5 + 3 = 8)
|
||||
cpu.current_op = Instruction::Add(5);
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 8);
|
||||
|
||||
println!("DEBUG: Arithmetic operations test completed successfully");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_4004_register_operations() {
|
||||
let mut cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
|
||||
|
||||
cpu.reset();
|
||||
|
||||
// Test register operations
|
||||
cpu.set_register(0, 0x0A).unwrap();
|
||||
cpu.set_register(1, 0x05).unwrap();
|
||||
|
||||
// Test LD (load from register 0 into accumulator) - 0x00 = LD 0
|
||||
cpu.current_op = Instruction::Ld(0);
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 0x0A);
|
||||
|
||||
// Test XCH (exchange with register 1) - 0x11 = XCH 1
|
||||
cpu.current_op = Instruction::Xch(1);
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 0x05); // ACC gets R1's value
|
||||
assert_eq!(cpu.get_register(0).unwrap(), 0x0A); // R0 gets old ACC value
|
||||
assert_eq!(cpu.get_register(1).unwrap(), 0x0A); // R1 gets new ACC value
|
||||
|
||||
println!("DEBUG: Register operations test completed successfully");
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Intel4004 {
|
||||
|
|
@ -1144,7 +1273,6 @@ impl Component for Intel4004 {
|
|||
self.prev_phi2 = phi2;
|
||||
|
||||
if phi1_rising {
|
||||
// Φ1 Rising Edge: Address phase (CPU drives bus) - handle address latching
|
||||
self.handle_phi1_rising();
|
||||
}
|
||||
|
||||
|
|
@ -1175,13 +1303,24 @@ impl Component for Intel4004 {
|
|||
self.current_instruction = instruction;
|
||||
self.current_op = self.decode_instruction(instruction);
|
||||
self.instruction_phase = InstructionPhase::Execute;
|
||||
|
||||
// Debug: Show instruction fetch details
|
||||
println!("DEBUG: [CPU] Fetched instruction 0x{:02X} from PC 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
|
||||
instruction, self.program_counter.value(), self.accumulator, self.full_address_ready);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstructionPhase::Execute => {
|
||||
// Execute the current instruction
|
||||
let old_pc = self.program_counter.value();
|
||||
self.execute_instruction();
|
||||
let new_pc = self.program_counter.value();
|
||||
|
||||
// Debug: Show instruction execution details
|
||||
println!("DEBUG: [CPU] Executed {:?} | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
|
||||
self.current_op, old_pc, new_pc, self.accumulator, self.full_address_ready);
|
||||
|
||||
self.instruction_phase = InstructionPhase::Fetch;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -742,6 +742,9 @@ impl Component for Intel4001 {
|
|||
/// Main update cycle - handles clock edge detection and operation dispatch
|
||||
/// Hardware: Responds to Φ1 and Φ2 clock edges from CPU
|
||||
fn update(&mut self) {
|
||||
if !self.is_running() {
|
||||
return;
|
||||
}
|
||||
// Handle both rising and falling edges for proper two-phase operation
|
||||
let phi1_rising = self.is_phi1_rising_edge(self.prev_phi1);
|
||||
let phi1_falling = self.is_phi1_falling_edge(self.prev_phi1);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ pub struct Intel4002 {
|
|||
// Instruction cycle tracking
|
||||
instruction_phase: bool, // Whether we're in instruction phase
|
||||
current_instruction: u8, // Current instruction being processed
|
||||
// Cycle counting for debug output
|
||||
cycle_count: u64, // Total number of clock cycles executed
|
||||
}
|
||||
|
||||
/// Intel 4002 RAM variants
|
||||
|
|
@ -210,6 +212,7 @@ impl Intel4002 {
|
|||
data_latch: None,
|
||||
instruction_phase: false,
|
||||
current_instruction: 0,
|
||||
cycle_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -416,10 +419,11 @@ impl Intel4002 {
|
|||
self.set_address_latch_time(None);
|
||||
self.set_address_high_nibble(None);
|
||||
self.set_address_low_nibble(None);
|
||||
self.set_full_address_ready(false);
|
||||
self.set_full_address_ready(false); // Reset address ready on reset
|
||||
self.data_latch = None;
|
||||
self.instruction_phase = false;
|
||||
self.current_instruction = 0;
|
||||
self.cycle_count = 0;
|
||||
|
||||
// Tri-state all outputs
|
||||
self.tri_state_data_bus();
|
||||
|
|
@ -437,6 +441,9 @@ impl Intel4002 {
|
|||
self.full_address_ready = true;
|
||||
self.address_latch_time = Some(Instant::now());
|
||||
|
||||
println!("DEBUG: [{}] Addr Assembled | High: 0x{:X} | Low: 0x{:X} | Full: 0x{:02X} | Ready: {} | Cycles: {}",
|
||||
self.base.name(), high, low, self.last_address, self.full_address_ready, self.get_cycle_count());
|
||||
|
||||
// Clear nibble storage for next address
|
||||
self.address_high_nibble = None;
|
||||
self.address_low_nibble = None;
|
||||
|
|
@ -563,7 +570,11 @@ impl Intel4002 {
|
|||
/// Transition to address phase state
|
||||
fn start_ram_address_phase(&mut self) {
|
||||
self.ram_state = RamState::AddressPhase;
|
||||
// Clear address state for new operation
|
||||
self.address_high_nibble = None;
|
||||
self.address_low_nibble = None;
|
||||
self.full_address_ready = false;
|
||||
self.last_address = 0;
|
||||
}
|
||||
|
||||
/// Handle address nibble latching during address phase
|
||||
|
|
@ -572,8 +583,12 @@ impl Intel4002 {
|
|||
|
||||
if self.address_high_nibble.is_none() {
|
||||
self.address_high_nibble = Some(nibble);
|
||||
println!("DEBUG: [{}] Addr Latch High | Nibble: 0x{:X} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), nibble, self.ram_state, self.get_cycle_count());
|
||||
} else if self.address_low_nibble.is_none() {
|
||||
self.address_low_nibble = Some(nibble);
|
||||
println!("DEBUG: [{}] Addr Latch Low | High: 0x{:X} | Low: 0x{:X} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), self.address_high_nibble.unwrap(), nibble, self.ram_state, self.get_cycle_count());
|
||||
self.assemble_full_address();
|
||||
self.start_latency_wait();
|
||||
}
|
||||
|
|
@ -627,11 +642,16 @@ impl Intel4002 {
|
|||
// First cycle - read from status character
|
||||
// For now, return status character 0 - this should be determined by instruction
|
||||
self.write_data_bus(self.status_characters[0]);
|
||||
println!("DEBUG: [{}] Status Char Read | Index: 0 | Data: 0x{:X} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), self.status_characters[0], self.ram_state, self.get_cycle_count());
|
||||
} else {
|
||||
// Second cycle - write to status character
|
||||
// For now, write to status character 0 - this should be determined by instruction
|
||||
let old_status = self.status_characters[0];
|
||||
self.status_characters[0] = data & 0x0F;
|
||||
self.ram_state = RamState::WriteData;
|
||||
println!("DEBUG: [{}] Status Char Write | Index: 0 | Old: 0x{:X} | New: 0x{:X} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), old_status, data & 0x0F, self.ram_state, self.get_cycle_count());
|
||||
}
|
||||
} else if address < 80 {
|
||||
// RAM operation
|
||||
|
|
@ -640,10 +660,15 @@ impl Intel4002 {
|
|||
// First cycle - read from RAM
|
||||
let ram_data = self.memory[address as usize];
|
||||
self.write_data_bus(ram_data);
|
||||
println!("DEBUG: [{}] RAM Read | Addr: 0x{:02X} | Data: 0x{:X} | Bank: {} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), address, ram_data, self.bank_select, self.ram_state, self.get_cycle_count());
|
||||
} else {
|
||||
// Second cycle - write to RAM
|
||||
let old_data = self.memory[address as usize];
|
||||
self.memory[address as usize] = data & 0x0F;
|
||||
self.ram_state = RamState::WriteData;
|
||||
println!("DEBUG: [{}] RAM Write | Addr: 0x{:02X} | Old: 0x{:X} | New: 0x{:X} | Bank: {} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), address, old_data, data & 0x0F, self.bank_select, self.ram_state, self.get_cycle_count());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -664,10 +689,15 @@ impl Intel4002 {
|
|||
// First cycle - read from output port (not typical for 4002)
|
||||
let port_data = self.output_ports[port];
|
||||
self.write_data_bus(port_data);
|
||||
println!("DEBUG: [{}] Output Port Read | Port: {} | Data: 0x{:X} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), port, port_data, self.ram_state, self.get_cycle_count());
|
||||
} else {
|
||||
// Second cycle - write to output port
|
||||
let data = self.read_data_bus();
|
||||
let old_port_data = self.output_ports[port];
|
||||
self.handle_output_port_operation(port, data);
|
||||
println!("DEBUG: [{}] Output Port Write | Port: {} | Old: 0x{:X} | New: 0x{:X} | State: {:?} | Cycles: {}",
|
||||
self.base.name(), port, old_port_data, data & 0x0F, self.ram_state, self.get_cycle_count());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -679,9 +709,8 @@ impl Intel4002 {
|
|||
fn return_to_idle(&mut self) {
|
||||
self.ram_state = RamState::Idle;
|
||||
self.address_latch_time = None;
|
||||
self.address_high_nibble = None;
|
||||
self.address_low_nibble = None;
|
||||
self.full_address_ready = false;
|
||||
// Don't reset address nibbles and ready flag immediately - keep them for potential reuse
|
||||
// They will be cleared when a new address operation starts
|
||||
self.data_latch = None;
|
||||
self.instruction_phase = false;
|
||||
self.current_instruction = 0;
|
||||
|
|
@ -730,6 +759,18 @@ impl Component for Intel4002 {
|
|||
/// Main update cycle - handles clock edge detection and operation dispatch
|
||||
/// Hardware: Responds to Φ1 and Φ2 clock edges from CPU
|
||||
fn update(&mut self) {
|
||||
if !self.is_running() {
|
||||
return;
|
||||
}
|
||||
// Debug: Show RAM state periodically to monitor for changes
|
||||
static mut CYCLE_COUNTER: u64 = 0;
|
||||
unsafe {
|
||||
CYCLE_COUNTER += 1;
|
||||
if CYCLE_COUNTER % 5000 == 0 { // Less frequent than CPU to avoid spam
|
||||
println!("DEBUG: [{}] RAM State | Bank: {} | Status: {:?} | Ready: {} | LastAddr: 0x{:02X} | Cycles: {} | HighNib: {:?} | LowNib: {:?}",
|
||||
self.base.name(), self.bank_select, self.ram_state, self.full_address_ready, self.last_address, self.get_cycle_count(), self.address_high_nibble, self.address_low_nibble);
|
||||
}
|
||||
}
|
||||
// Handle both rising and falling edges for proper two-phase operation
|
||||
let (phi1, phi2) = self.read_clock_pins();
|
||||
let phi1_rising = phi1 == PinValue::High && self.prev_phi1 == PinValue::Low;
|
||||
|
|
@ -768,6 +809,9 @@ impl Component for Intel4002 {
|
|||
let instruction = self.read_data_bus();
|
||||
self.handle_special_instructions(instruction);
|
||||
}
|
||||
|
||||
// Increment cycle count for debug output
|
||||
self.cycle_count += 1;
|
||||
}
|
||||
|
||||
/// Run component in time-slice mode (manual control)
|
||||
|
|
@ -903,6 +947,12 @@ impl Intel4002 {
|
|||
self.variant
|
||||
}
|
||||
|
||||
/// Get the current cycle count for the RAM component
|
||||
/// Returns: Current cycle count since component start
|
||||
pub fn get_cycle_count(&self) -> u64 {
|
||||
self.cycle_count
|
||||
}
|
||||
|
||||
/// Clear all RAM to zero
|
||||
pub fn clear_ram(&mut self) {
|
||||
self.memory = [0u8; 80]; // Clear 80 nibbles
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue