// Generates all the address nodes of the stack and adds them to the page const generateAddressNodes = () => { // Generate title box first const titleNode = document.createElement('div'); titleNode.classList.add('titleNode'); titleNode.innerText = "Address"; stack.appendChild(titleNode); let num = 1; for (let stackaddr = stackBase; stackaddr < stackBase + nStackElements * 4; stackaddr += 4) { const addressNode = document.createElement('div'); addressNode.classList.add('addressBox'); addressNode.innerText = '0x' + stackaddr.toString(16).toUpperCase(); stack.appendChild(addressNode); const stackOffset = stackNodeSize * (num++ + 1 / 2); stackBlockToCanvasPos[addressNode.innerText.toLowerCase()] = stackOffset; } }; // Refreshes the canvas containing all the arrow pointers const refreshCanvas = () => { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.lineWidth = 3; // Definitely not code to draw an arrow by hand in a canvas : - ) const arrowWidth = 8; let i = 0; for (key in lines) { const exitPos = key; const entryPos = lines[key] - stackNodeSize / 2 + key / stackNodeSize * 10; ctx.beginPath(); ctx.moveTo(0, exitPos); ctx.lineTo(15 + 10 * i, exitPos); ctx.lineTo(15 + 10 * i, entryPos); ctx.lineTo(0, entryPos); ctx.lineTo(arrowWidth, entryPos + arrowWidth); ctx.moveTo(0, entryPos); ctx.lineTo(arrowWidth, entryPos - arrowWidth); ctx.stroke(); i++; } }; // Sets up listener on the given pointer variable of the stack to draw arrows // in real-time const setupPointerListener = (newStackBox, addr) => { const inputText = newStackBox.querySelector('input[name="stackValue"]'); inputText.addEventListener('input', () => { let startY = stackBlockToCanvasPos[addr]; let endY = stackBlockToCanvasPos[inputText.value.toLowerCase()]; if (endY === undefined) { delete lines[startY]; } else { lines[startY] = endY; } refreshCanvas(); }); }; // Generates a single variable node for the stack, adds a listener for drawing // if the variable is a pointer. const generateAndBindVariableNode = (type, name, addr) => { const stackBoxTemplate = document.getElementById("stackBoxTemplate"); const newStackBox = stackBoxTemplate.cloneNode(true); newStackBox.removeAttribute('id'); newStackBox.getElementsByClassName('stackBoxType')[0].innerText += type; newStackBox.getElementsByClassName('stackBoxName')[0].innerText += name; stack.appendChild(newStackBox); if (type.includes('*')) { setupPointerListener(newStackBox, addr); } }; // Generates all variable nodes to be added to the stack on the page const generateVariableNodes = () => { // Generate title box first const titleNode = document.createElement('div'); titleNode.classList.add('titleNode'); titleNode.innerText = "Variable"; stack.appendChild(titleNode); // Then generate variables nodes generateAndBindVariableNode('???', '???', '0xff80'); generateAndBindVariableNode('int', 'n', '0xff84'); generateAndBindVariableNode('int *', 'p', '0xff88'); generateAndBindVariableNode('int *', 'q', '0xff8c'); generateAndBindVariableNode('???', '???', '0xff90'); }; // Generates the entire stack visible on the page const generateStack = () => { generateAddressNodes(); generateVariableNodes(); }; const stack = document.getElementById('stackGrid'); const canvas = document.getElementById('stackCanvas') const instructions = document.getElementById('instructionsBox') const stackBase = 0xFF80; const nStackElements = 5; let currentInstructionIndex = 0; const stackNodeSize = stack.clientHeight / (nStackElements + 1) const stackBlockToCanvasPos = new Object(); const lines = new Object(); generateStack(); // Add a simple listener to cycle through the code instructions document.getElementById('changeCurrentCodeLineButton').addEventListener('click', () => { instructions.children[currentInstructionIndex++].removeAttribute('id'); currentInstructionIndex %= instructions.children.length; instructions.children[currentInstructionIndex].setAttribute('id', 'selectedInstruction') });