Separated two-phase clock from generic clock

This commit is contained in:
Jan-Peter von Hunnius 2025-09-30 07:22:48 +02:00
parent 7d06837608
commit c8552a1488
11 changed files with 565 additions and 77 deletions

View file

@ -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