Skip to content

Instantly share code, notes, and snippets.

@andreisuslov
Last active July 6, 2024 20:38
Show Gist options
  • Save andreisuslov/7e2cbf0b28cd660c329741e895ea28e6 to your computer and use it in GitHub Desktop.
Save andreisuslov/7e2cbf0b28cd660c329741e895ea28e6 to your computer and use it in GitHub Desktop.
A visual representation of a simple CPU architecture, demonstrating the fetch-decode-execute cycle. Features a programmable RAM, step-by-step execution via clock ticks, and real-time updates of CPU components. To play with this code, follow this link: https://html-preview.github.io/?url=https://gist.githubusercontent.com/andreisuslov/7e2cbf0b28c…
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enhanced CPU Simulator with Collapsible Explanation</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
.container {
width: 1200px;
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
.simulator {
display: flex;
height: 600px;
}
.cpu, .ram {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
}
.cpu {
background-color: #e0e0e0;
border-top-left-radius: 8px;
}
h2 {
font-size: 24px;
margin-bottom: 20px;
}
.register {
background-color: white;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.register h3 {
font-size: 16px;
margin-bottom: 5px;
}
.register-value {
border: 1px solid #ccc;
padding: 5px;
text-align: center;
}
.execution-phase {
background-color: #e3f2fd;
padding: 5px;
margin-bottom: 5px;
border-radius: 4px;
}
.active-phase {
font-weight: bold;
}
button {
padding: 10px;
margin-top: 10px;
background-color: #2196f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
width: 100%;
box-sizing: border-box;
height: 38px; /* Ensure they have the same height */
margin-bottom: 5px;
}
button:hover {
background-color: #1976d2;
}
.ram-table-container {
flex-grow: 1;
overflow-y: auto;
margin-top: 10px;
}
table {
width: 100%;
border-collapse: collapse;
}
thead {
position: sticky;
top: 0;
background-color: white;
}
th, td {
text-align: left;
padding: 8px;
border-bottom: 1px solid #ddd;
}
th {
font-weight: bold;
}
.input-group {
display: flex;
justify-content: space-between;
}
select, input {
margin-top: 10px;
width: 100%;
padding: 5px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
height: 38px; /* Ensure they have the same height */
font-size: 16px; /* Ensure they have the same font size */
}
select {
margin-bottom: 5px;
}
.mode-switch {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
}
.mode-switch button {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
padding: 0;
background-color: #2196f3;
border-radius: 50%;
}
.mode-switch button:hover {
background-color: #1976d2;
}
.mode-switch button svg {
width: 24px;
height: 24px;
}
.hidden {
display: none;
}
#explanationToggle {
width: 100%;
margin-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.explanation {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
padding: 0 20px; /* Add padding for better readability */
box-sizing: border-box;
}
.explanation.visible {
max-height: 1000px; /* Adjust this value based on your content */
transition: max-height 0.5s ease-in;
padding: 20px; /* Add padding for better readability */
}
.explanation h2 {
font-size: 22px;
margin-bottom: 15px;
}
.explanation p, .explanation ul, .explanation ol {
margin-bottom: 10px;
line-height: 1.6;
}
.explanation ul, .explanation ol {
padding-left: 20px;
}
.explanation ul li, .explanation ol li {
margin-bottom: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="simulator">
<div class="cpu">
<h2>CPU</h2>
<div class="register">
<h3>Programme Counter</h3>
<div id="pc" class="register-value">0</div>
</div>
<div class="register">
<h3>Instruction Register</h3>
<div id="ir" class="register-value"></div>
</div>
<div class="register">
<h3>Accumulator</h3>
<div id="accumulator" class="register-value">0</div>
</div>
<div id="execution-phases">
<div class="execution-phase active-phase">► Fetch</div>
<div class="execution-phase">Decode</div>
<div class="execution-phase">Execute</div>
</div>
<button id="clockTick" onclick="clockTick()">Clock Tick</button>
</div>
<div class="ram">
<div class="mode-switch">
<button id="modeToggle" onclick="toggleMode()">
<svg id="editIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
<svg id="viewIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="hidden">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
</div>
<h2>RAM</h2>
<div class="ram-table-container">
<table id="ram-table">
<thead>
<tr>
<th>Address</th>
<th>Value</th>
<th class="edit-column hidden">Edit</th>
</tr>
</thead>
<tbody>
<!-- RAM content will be populated here -->
</tbody>
</table>
</div>
</div>
</div>
<button id="explanationToggle" onclick="toggleExplanation()">Show Explanation</button>
<div id="explanation" class="explanation">
<h2>What is this and how does it work?</h2>
<p>This simulator demonstrates the basic functioning of a Central Processing Unit (CPU) in a computer. At its core, every modern computer operates on similar principles, no matter how complex its tasks may seem.</p>
<p>The simulation shows three main components:</p>
<ul>
<li><strong>CPU:</strong> The brain of the computer, containing registers for temporary data storage and processing.</li>
<li><strong>RAM (Random Access Memory):</strong> Where instructions and data are stored for quick access.</li>
<li><strong>Clock:</strong> Represented by the "Clock Tick" button, it drives the CPU through its cycle.</li>
</ul>
<p>The CPU operates in a continuous "Fetch-Decode-Execute" cycle:</p>
<ol>
<li><strong>Fetch:</strong> The CPU retrieves an instruction from RAM.</li>
<li><strong>Decode:</strong> The CPU interprets the instruction.</li>
<li><strong>Execute:</strong> The CPU carries out the instruction.</li>
</ol>
<p>In this simulator, you can see this cycle in action. The program loaded into RAM is a simple counting program. With each complete cycle, it increments a value stored in RAM.</p>
<p>While this simulation runs at a speed we can observe, real CPUs execute billions of instructions per second. This incredible speed allows computers to perform complex tasks like playing video games or displaying this web page.</p>
<p>You can interact with this simulator by clicking the "Clock Tick" button to step through each phase of the cycle, or by switching to "Edit Mode" to modify the instructions in RAM.</p>
</div>
</div>
<script>
let pc = 0;
let ir = '';
let accumulator = 0;
let ram = [
{ address: 0, value: 'LOAD 6' },
{ address: 1, value: 'ADD 7' },
{ address: 2, value: 'STORE 6' },
{ address: 3, value: 'JUMP 1' },
{ address: 4, value: '0' },
{ address: 5, value: '0' },
{ address: 6, value: '1' },
{ address: 7, value: '1' },
{ address: 8, value: '0' },
{ address: 9, value: '0' },
{ address: 10, value: '0' },
{ address: 11, value: '0' },
{ address: 12, value: '0' },
{ address: 13, value: '0' },
{ address: 14, value: '0' },
{ address: 15, value: '0' },
];
let executionPhase = 'Fetch';
let currentMode = 'display';
const instructions = ['', 'LOAD', 'ADD', 'STORE', 'JUMP'];
function updateDisplay() {
document.getElementById('pc').textContent = pc;
document.getElementById('ir').textContent = ir;
document.getElementById('accumulator').textContent = accumulator;
const phases = document.getElementById('execution-phases').children;
for (let i = 0; i < phases.length; i++) {
phases[i].classList.remove('active-phase');
phases[i].textContent = phases[i].textContent.replace('► ', '');
}
const activePhase = Array.from(phases).find(phase => phase.textContent.includes(executionPhase));
activePhase.classList.add('active-phase');
activePhase.textContent = '► ' + activePhase.textContent;
updateRAMTable();
}
function updateRAMTable() {
const ramTable = document.getElementById('ram-table').getElementsByTagName('tbody')[0];
ramTable.innerHTML = '';
for (const item of ram) {
const row = ramTable.insertRow();
row.insertCell(0).textContent = item.address;
if (currentMode === 'edit') {
const cell = row.insertCell(1);
const [instruction, operand] = item.value.split(' ');
const select = document.createElement('select');
instructions.forEach(instr => {
const option = document.createElement('option');
option.value = instr;
option.textContent = instr || '(empty)';
option.selected = instr === instruction;
select.appendChild(option);
});
const input = document.createElement('input');
input.type = 'text';
input.value = operand || '';
cell.appendChild(select);
cell.appendChild(input);
const editCell = row.insertCell(2);
const saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.onclick = () => saveInstruction(item.address, select.value, input.value);
editCell.appendChild(saveButton);
} else {
row.insertCell(1).textContent = item.value || '0';
}
}
}
function executeInstruction() {
switch (executionPhase) {
case 'Fetch':
ir = ram[pc].value;
executionPhase = 'Decode';
break;
case 'Decode':
executionPhase = 'Execute';
break;
case 'Execute':
const [operation, operand] = ir.split(' ');
switch (operation) {
case 'LOAD':
accumulator = parseInt(ram[parseInt(operand)].value) || 0;
break;
case 'ADD':
accumulator += parseInt(ram[parseInt(operand)].value) || 0;
break;
case 'STORE':
ram[parseInt(operand)].value = accumulator.toString();
break;
case 'JUMP':
pc = (parseInt(operand) - 1 + ram.length) % ram.length; // Ensure PC stays within bounds
break;
}
pc = (pc + 1) % ram.length;
executionPhase = 'Fetch';
break;
}
updateDisplay();
}
function clockTick() {
if (currentMode === 'edit') {
toggleMode();
}
executeInstruction();
}
function toggleMode() {
currentMode = currentMode === 'display' ? 'edit' : 'display';
const editColumns = document.getElementsByClassName('edit-column');
for (let col of editColumns) {
col.classList.toggle('hidden', currentMode === 'display');
}
document.getElementById('editIcon').classList.toggle('hidden', currentMode === 'edit');
document.getElementById('viewIcon').classList.toggle('hidden', currentMode === 'display');
updateDisplay();
}
function saveInstruction(address, instruction, operand) {
if (instruction) {
ram[address].value = `${instruction} ${operand}`.trim();
} else {
ram[address].value = operand.trim() || '0';
}
updateDisplay();
}
function toggleExplanation() {
const explanation = document.getElementById('explanation');
const button = document.getElementById('explanationToggle');
if (explanation.classList.contains('visible')) {
explanation.classList.remove('visible');
button.textContent = 'Show Explanation';
} else {
explanation.classList.add('visible');
button.textContent = 'Hide Explanation';
}
}
function updateRAMTable() {
const ramTable = document.getElementById('ram-table').getElementsByTagName('tbody')[0];
ramTable.innerHTML = '';
for (const item of ram) {
const row = ramTable.insertRow();
row.insertCell(0).textContent = item.address;
if (currentMode === 'edit') {
const cell = row.insertCell(1);
const [instruction, operand] = item.value.split(' ');
const inputGroup = document.createElement('div');
inputGroup.className = 'input-group';
const select = document.createElement('select');
instructions.forEach(instr => {
const option = document.createElement('option');
option.value = instr;
option.textContent = instr || '(empty)';
option.selected = instr === instruction;
select.appendChild(option);
});
const input = document.createElement('input');
input.type = 'text';
input.value = operand || '';
inputGroup.appendChild(select);
inputGroup.appendChild(input);
cell.appendChild(inputGroup);
const editCell = row.insertCell(2);
const saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.onclick = () => saveInstruction(item.address, select.value, input.value);
editCell.appendChild(saveButton);
} else {
row.insertCell(1).textContent = item.value || '0';
}
}
}
// Initialize the display
updateDisplay();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment