// Clock simulation engine import { state } from './state.js'; import { evaluateAll } from './gates.js'; import { forceRecordSample } from './waveform.js'; export function simTick() { // Toggle all CLOCK gates state.gates.forEach(g => { if (g.type === 'CLOCK') { g.value = g.value ? 0 : 1; } }); evaluateAll(); // Force record even if evaluateAll didn't detect change if (state.recording && state.waveformVisible) { forceRecordSample(); } } export function startSim() { if (state.simRunning) return; const hasClocks = state.gates.some(g => g.type === 'CLOCK'); if (!hasClocks) { alert('Place a CLOCK gate first!'); return; } state.simRunning = true; // Auto-open waveform panel if (!state.waveformVisible) { state.waveformVisible = true; document.getElementById('waveform-panel').classList.add('visible'); document.getElementById('sim-btn').classList.add('active'); // Trigger resize via event so renderer picks it up window.dispatchEvent(new Event('resize')); } state.simInterval = setInterval(simTick, state.simSpeed); updateSimUI(); } export function stopSim() { state.simRunning = false; if (state.simInterval) clearInterval(state.simInterval); state.simInterval = null; updateSimUI(); } export function updateSimUI() { const btn = document.getElementById('sim-run-btn'); if (state.simRunning) { btn.textContent = '⏹ Stop'; btn.classList.add('active'); } else { btn.textContent = '▶ Run'; btn.classList.remove('active'); } document.getElementById('sim-speed-label').textContent = `${state.simSpeed}ms`; } export function adjustSpeed(delta) { state.simSpeed = Math.max(50, Math.min(2000, state.simSpeed + delta)); if (state.simRunning) { clearInterval(state.simInterval); state.simInterval = setInterval(simTick, state.simSpeed); } updateSimUI(); }