/** * The full computer object class */ //Global variables var COMPONENT_SPACING = "20px"; var ANIMATE_NONE = 0, ANIMATE_FLOW = 1, ANIMATE_DATA = 2; var PROGRAM_FLOW_SPEED_COEFFICIENT = 400; var VICSERVER_DIR = "../VICServer/"; var READ_ONLY_MEM = [ 98, 99 ]; //Instruction register label texts var INSTRUCTIONS_TEXT = []; INSTRUCTIONS_TEXT[0] = "עצור
 "; INSTRUCTIONS_TEXT[1] = "הוסף ערך תא זיכרון {0}
לערך אוגר הנתון"; INSTRUCTIONS_TEXT[2] = "הפחת ערך תא זיכרון {0}
מערך אוגר הנתון"; INSTRUCTIONS_TEXT[3] = "טען ערך תא זיכרון {0}
אל אוגר הנתון"; INSTRUCTIONS_TEXT[4] = "שמור ערך אוגר הנתון
בתא זיכרון {0}"; INSTRUCTIONS_TEXT[5] = "עבור לשורה {0}
 "; INSTRUCTIONS_TEXT[6] = "אם ערך אוגר הנתון
שווה ל-0, עבור לשורה {0}"; INSTRUCTIONS_TEXT[7] = "אם ערך אוגר הנתון
גדול מ-0, עבור לשורה {0}"; INSTRUCTIONS_TEXT[8] = "קרא את הקלט הבא
אל אוגר הנתון"; INSTRUCTIONS_TEXT[9] = "כתוב את ערך
אוגר הנתון לפלט"; //"קבע את ערך אוגר
הנתון לערך {0}"; //Status bar texts var MSG_PROGRAM_STOP = "התוכנית הופסקה באמצעות פקודת 'עצור'"; var MSG_NO_INPUT = "תקלה: חסר קלט"; var MSG_OVERFLOW = "תקלה: חסר קלט"; var MSG_OVERFLOW_RAM = "תא זיכרון"; var MSG_OVERFLOW_DATA = "אוגר הנתון"; var MSG_PROGRAM_TOO_LARGE = "תקלה: לא ניתן לטעון את התוכנית מכיוון שהיא גדולה מדי להכנס בזיכרון"; var PC_TOO_LARGE = "תקלה: הרצת התוכנית הופסקה לאחר שמונה הפקודות עבר את הערך 99"; var MSG_INVALID_CODE = "תקלה: קוד לא חוקי"; var MSG_PROGRAM_LOADED = "התוכנית נטענה בהצלחה"; var MSG_MEM_READONLY = "תקלה: הרצת התוכנית הופסקה מכיוון שתא זיכרון {0} מוגן מפני כתיבה"; var CLEAR_IO = 1; var CLEAR_MEMORY = 2; var CLEAR_HIGH_MEMORY = 3; var CLEAR_ALL = 4; /** * Constructor * container - A container object for the computer */ function Computer(container) { //Build the HTML DOM //Build frame var frame = document.createElement("TABLE"); frame.cellPadding = "0"; frame.cellSpacing = "0"; var tr0 = frame.insertRow(0); var htd = tr0.insertCell(0); htd.style.backgroundColor = "#000080"; htd.colSpan = "3"; htd.innerHTML = "The Visual Computer" htd.style.textAlign = "Left"; htd.style.fontSize = "12px"; htd.style.fontFamily = "Arial"; htd.style.fontWeight = "bold"; htd.style.padding = "3px"; htd.style.color = "#FFFFFF"; //htd.style.letterSpacing = "5px"; var row0 = frame.insertRow(1); row0.style.height = "9px"; var r0d0 = row0.insertCell(0); r0d0.style.width = "9px"; r0d0.style.backgroundImage = "url(../Borders/computer_topleft.gif)"; var r0d1 = row0.insertCell(1); r0d1.style.backgroundImage = "url(../Borders/computer_top.gif)"; var r0d2 = row0.insertCell(2); r0d2.style.width = "9px"; r0d2.style.backgroundImage = "url(../Borders/computer_topright.gif)"; var row1 = frame.insertRow(2); row1.style.height = "9px"; var r1d0 = row1.insertCell(0); r1d0.style.width = "9px"; r1d0.style.backgroundImage = "url(../Borders/computer_left.gif)"; var frameContent = row1.insertCell(1); var r1d2 = row1.insertCell(2); r1d2.style.width = "9px"; r1d2.style.backgroundImage = "url(../Borders/computer_right.gif)"; var row2 = frame.insertRow(3); row2.style.height = "9px"; var r2d0 = row2.insertCell(0); r2d0.style.width = "9px"; r2d0.style.backgroundImage = "url(../Borders/computer_bottomleft.gif)"; var r2d1 = row2.insertCell(1); r2d1.style.backgroundImage = "url(../Borders/computer_bottom.gif)"; var r2d2 = row2.insertCell(2); r2d2.style.width = "9px"; r2d2.style.backgroundImage = "url(../Borders/computer_bottomright.gif)"; container.appendChild(frame); //Build computer GUI var table = document.createElement("TABLE"); var tr = table.insertRow(0); tr.style.verticalAlign = "top"; var ioContainer = tr.insertCell(0); tr.insertCell(1).style.width = COMPONENT_SPACING; tr.cells[1].style.borderRightWidth = "1"; tr.cells[1].style.borderRightStyle = "Dashed"; tr.cells[1].style.borderRightColor = "#000000"; tr.cells[1].innerHTML = " "; var cpuContainer = tr.insertCell(2); tr.insertCell(3).style.width = COMPONENT_SPACING; tr.cells[3].style.borderLeftWidth = "1"; tr.cells[3].style.borderLeftStyle = "Dashed"; tr.cells[3].style.borderLeftColor = "#000000"; tr.cells[3].style.width = "20px"; tr.cells[3].innerHTML = " "; var memoryContainer = tr.insertCell(4); frameContent.appendChild(table); this.parentElement = container; //Components names var r_names = table.insertRow(1); var d_names = r_names.insertCell(0); d_names.colSpan = "5"; d_names.style.borderColor = "#000000"; d_names.style.borderStyle = "Solid"; d_names.style.borderWidth = "1px"; d_names.style.backgroundColor = "#C0FFFF" var dnt = document.createElement("TABLE"); dnt.style.width = "100%"; dnt.style.fontSize = "14px"; dnt.style.fontWeight = "bold"; var dntr = dnt.insertRow(0); var dntr0 = dntr.insertCell(0); dntr0.innerHTML = "קלט / פלט"; dntr0.style.width = "70px"; var dntr1 = dntr.insertCell(1); dntr1.innerHTML = "עיבוד"; dntr1.style.width = "120px"; var dntr2 = dntr.insertCell(2); dntr2.innerHTML = "זיכרון"; dntr2.style.width = "100px"; dntr0.style.textAlign = dntr1.style.textAlign = dntr2.style.textAlign = "Center"; d_names.appendChild(dnt); //Status bar var r_status = table.insertRow(2); var d_status = r_status.insertCell(0); d_status.colSpan = "5"; d_status.align = "center"; var statusBar = document.createElement("INPUT"); statusBar.type = "TEXT"; statusBar.style.fontSize = "12px"; statusBar.style.fontFamily = "Arial"; statusBar.style.fontWeight = "bolder"; statusBar.readOnly = true; statusBar.style.backgroundColor = "#D4D0C8"; statusBar.style.width = "100%"; statusBar.style.textAlign = "right"; statusBar.dir = "rtl"; d_status.appendChild(statusBar); /** * Sets the status bar text and color * message - Text to display * color - Optional. A String with the color value (e.g. "#FF0000" or "Red") */ setStatus = function(message, color) { var col = "#000000"; if (color) col = color; statusBar.value = message; statusBar.style.color = col; } //Define chips (IO, Memory, CPU) var ioChip = new IOChip(ioContainer); var cpuChip = new CPUChip(cpuContainer); var memoryChip = new MemoryChip(memoryContainer); cpuChip.setComputer(this); this.width = 700; var that = this; var animation = ANIMATE_DATA; var isRunning = false; var nextStep; var slider, controls; /** * Enables/disables controls and components */ function enable(value) { controls.enabled = value; ioChip.enable(value); cpuChip.enable(value); memoryChip.enable(value); } /** * Sets the animation type for this computer */ this.setAnimation = function (animationType) { if (animationType == ANIMATE_NONE || animationType == ANIMATE_FLOW || animationType == ANIMATE_DATA) animation = animationType; } this.getAnimation = function () { return animation; } /* * The following private functions handles highlighting of memory and CPU cells * (Some of them have been disabled) */ var lastLowMemoryHighlight = null; function highlightLowMemory(address) { dehighlightLowMemory(); lastLowMemoryHighlight = memoryChip.getMemoryCell(address); if (lastLowMemoryHighlight) lastLowMemoryHighlight.highlight(true); } function dehighlightLowMemory() { if (lastLowMemoryHighlight) lastLowMemoryHighlight.highlight(false); else { for (var i = 0; i < 50; i++) memoryChip.getMemoryCell(i).highlight(false); } } var lastHighMemoryHighlight = null; function highlightHighMemory(address) { if (address < 50) { highlightLowMemory(address); return; } dehighlightHighMemory(); lastHighMemoryHighlight = memoryChip.getMemoryCell(address); lastHighMemoryHighlight.highlight(true); dehighlightHighMemory(); } function dehighlightHighMemory() { if (lastHighMemoryHighlight) lastHighMemoryHighlight.highlight(false); } var lastCPUHighlight = null; function highlightCPU(regid) { return; dehighlightCPU(); lastCPUHighlight = cpuChip.getCellAt(regid); lastCPUHighlight.highlight(true); } function dehighlightCPU() { return; if (lastCPUHighlight) lastCPUHighlight.highlight(false); } /* * End of private functions for handling highlighting */ _oldAnimation = null; /** * Performs a Fetch operation * Reads the next instruction from memory and advances PC by 1 */ this.fetch = function(single, oldAnimation, fly) { if (single) _oldAnimation = oldAnimation; else _oldAnimation = undefined; if (animation == ANIMATE_NONE && _startTime) { if (((new Date()) - _startTime) > 10000) { var res = confirm("נראה כי משך ריצת התוכנית הינו ארוך באופן חריג. יתכן שהתוכנית נכנסה ללולאה אינסופית. לחץ 'אישור' להפסקת התוכנית או 'ביטול' להמשך הרצתה."); if (res) { that.stop(false, true); } } } enable(false); var address = cpuChip.getPCValue(); if (address >= 100) { setStatus(PC_TOO_LARGE, "#FF0000"); that.stop(); return; } fetches++; var memCell = memoryChip.getMemoryCell(address); if (memCell) { highlightLowMemory(address); highlightCPU(0); if (fly && animation == ANIMATE_DATA) { var fdata = new FlyingData(memCell, cpuChip.getCellAt(0), memCell.getAbsoluteLeft(), memCell.getAbsoluteTop(), cpuChip.getCellAt(0).getAbsoluteLeft(), cpuChip.getCellAt(0).getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(fetchDone); } else { if (fetches > 50) { setTimeout(fetchDone, 0); fetches = 0; } else fetchDone(); } } function fetchDone() { cpuChip.getCellAt(0).setValue(memCell.getValue()); cpuChip.advancePC(); if (!isRunning && !single) enable(true); that.decode(false, single); } } /** * Performs a READ operation * Reads data from input stream to data register */ this.read = function () { var input = ioChip.read(); if (input && input.getValue() != undefined) { highlightCPU(1); if (animation == ANIMATE_DATA) { var fdata = new FlyingData(input, cpuChip.getCellAt(1), input.getAbsoluteLeft(), input.getAbsoluteTop(), cpuChip.getCellAt(1).getAbsoluteLeft(), cpuChip.getCellAt(1).getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(readDone); } else { readDone(); } } else { setStatus(MSG_NO_INPUT, "#FF0000"); that.stop(); } function readDone() { cpuChip.getCellAt(1).setValue(input.getValue()); executeDone(); } } /** * Performs a WRITE operation * Writes data from data register to output stream */ this.write = function () { var output = ioChip.getCurrentOutput(); if (output) { highlightCPU(1); output.highlight(true); if (animation == ANIMATE_DATA) { var fdata = new FlyingData(cpuChip.getCellAt(1), output, cpuChip.getCellAt(1).getAbsoluteLeft(), cpuChip.getCellAt(1).getAbsoluteTop(), output.getAbsoluteLeft(), output.getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(writeDone); } else writeDone(); } function writeDone() { ioChip.write(cpuChip.getCellAt(1).getValue()); executeDone(); } } /** * Resets the computer * Resets I/O, program counter, registers */ this.reset = function() { var anim = animation; animation = ANIMATE_DATA; ioChip.reset(); //memoryChip.reset(); for (var i = 0; i <= 2; i++) cpuChip.getCellAt(i).setValue(0); cpuChip.setInstructionLabel("

"); setStatus("", null); dehighlightLowMemory(); dehighlightHighMemory(); dehighlightCPU(); lastLowMemoryHighlight = null; lastHighMemoryHighlight = null; lastCPUHighlight = null; memoryChip.getMemoryCell(98).setValue(0); memoryChip.getMemoryCell(98).enabled = false; memoryChip.getMemoryCell(99).setValue(1); memoryChip.getMemoryCell(99).enabled = false; highlightLowMemory(0); nextStep = that.fetch; animation = anim; } /** * Performs LOAD operation * Loads a value from the specified memory address to the data register * address - address to read */ this.load = function(address) { var memCell = memoryChip.getMemoryCell(address); if (memCell) { highlightHighMemory(address); highlightCPU(1); if (animation == ANIMATE_DATA) { var fdata = new FlyingData(memCell, cpuChip.getCellAt(1), memCell.getAbsoluteLeft(), memCell.getAbsoluteTop(), cpuChip.getCellAt(1).getAbsoluteLeft(), cpuChip.getCellAt(1).getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(loadDone); } else loadDone(); } function loadDone() { cpuChip.getCellAt(1).setValue(memCell.getValue()); executeDone(); } } /** * Performs STORE operation * Stores a value from the data register to the specified memory address * address - address to store the data in */ this.store = function(address) { for (var i = 0; i < READ_ONLY_MEM.length; i++) { if (READ_ONLY_MEM[i] == address) { setStatus(MSG_MEM_READONLY.replace("{0}", address), "#FF0000"); that.stop(); return; } } var memCell = memoryChip.getMemoryCell(address); if (memCell) { highlightHighMemory(address); highlightCPU(1); if (animation == ANIMATE_DATA) { var fdata = new FlyingData(cpuChip.getCellAt(1), memCell, cpuChip.getCellAt(1).getAbsoluteLeft(), cpuChip.getCellAt(1).getAbsoluteTop(), memCell.getAbsoluteLeft(), memCell.getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(storeDone); } else storeDone(); } function storeDone() { memCell.setValue(cpuChip.getCellAt(1).getValue()); executeDone(); } } /** * Performs ADD operation * Adds a value from the specified memory address to the current data register value * address - address to read */ this.add = function(address) { var memCell = memoryChip.getMemoryCell(address); if (memCell) { highlightHighMemory(address); highlightCPU(1); if (animation == ANIMATE_DATA) { var fdata = new FlyingData(memCell, cpuChip.getCellAt(1), memCell.getAbsoluteLeft(), memCell.getAbsoluteTop(), cpuChip.getCellAt(1).getAbsoluteLeft(), cpuChip.getCellAt(1).getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(addDone); } else addDone(); } function addDone() { var res = cpuChip.getCellAt(1).setValue(cpuChip.getCellAt(1).getValue() + memCell.getValue()); if (res && res.overflow) { setStatus(MSG_OVERFLOW.replace("{0}", MSG_OVERFLOW_DATA), "#FF0000"); that.stop(); } executeDone(); } } /** * Performs SUB operation * Subtract the value in the specified memory address from the current data register value * address - address to read */ this.sub = function(address) { var memCell = memoryChip.getMemoryCell(address); if (memCell) { highlightHighMemory(address); highlightCPU(1); if (animation == ANIMATE_DATA) { var fdata = new FlyingData(memCell, cpuChip.getCellAt(1), memCell.getAbsoluteLeft(), memCell.getAbsoluteTop(), cpuChip.getCellAt(1).getAbsoluteLeft(), cpuChip.getCellAt(1).getAbsoluteTop(), parseInt(CELL_HEIGHT) * 2 + "px", CELL_HEIGHT, false, slider.getValue()); fdata.fly(subDone); } else subDone(); } function subDone() { var res = cpuChip.getCellAt(1).setValue(cpuChip.getCellAt(1).getValue() - memCell.getValue()); if (res && res.overflow) { setStatus(MSG_OVERFLOW.replace("{0}", MSG_OVERFLOW_DATA), "#FF0000"); that.stop(); } executeDone(); } } /** * Highlights instruction memory according to PC * (To be called after manual change of PC) */ this.highlightPCAddress = function() { highlightLowMemory(cpuChip.getCellAt(2).getValue()); nextStep = that.fetch; if (controls) controls.setFetch(); } /** * Sets the next step to 'Execute' * (To be called after manual change of instruction register) */ this.setExecute = function() { nextStep = that.execute; if (controls) controls.setExecute(); } /** * Performs GOTO operation * Jumps to the specified instruction address by changing PC */ this.goto = function(address) { highlightCPU(1); highlightLowMemory(address); cpuChip.getCellAt(2).setValue(address); executeDone(); } /** * Performs GTZ operation * If data register value is zero, jumps to the specified instruction address by changing PC */ this.gotoZero = function(address) { if (cpuChip.getCellAt(1).getValue() == 0) that.goto(address); else executeDone(); } /** * Performs GTP operation * If data register value is grater than zero, jumps to the specified instruction address * by changing PC */ this.gotoGreater = function(address) { if (cpuChip.getCellAt(1).getValue() > 0) that.goto(address); else executeDone(); } /** * Performs SET operation * Sets data register to the specified value */ this.set = function(value) { highlightCPU(1); if (animation == ANIMATE_DATA) setTimeout(setDone, 500); else setDone(); function setDone() { cpuChip.getCellAt(1).setValue(value); executeDone(); } } /** * Performs STOP operation * Stops execution of current program */ this.stop = function(normal, fromPanel) { isRunning = false; if (normal) setStatus(MSG_PROGRAM_STOP, null); if (controls) controls.setFetch(true); nextStep = that.fetch; if (_oldAnimation) animation = _oldAnimation; if (animation == ANIMATE_NONE) showValues(); enable(true); if (controls && (!fromPanel)) controls.stopAll(); } /** * Decodes instruction in instruction register */ this.decode = function(autoExecute, single) { var instruction = cpuChip.getCellAt(0).getValue(); var opCode = Math.floor(instruction / 100); var imm = instruction % 100; var callee; var param; var text = ""; try { text = INSTRUCTIONS_TEXT[opCode]; } catch (e) { } switch (opCode) { case 1: //ADD callee = that.add; param = imm; break; case 2: //SUB callee = that.sub; param = imm; break; case 3: //LOAD callee = that.load; param = imm; break; case 4: //STORE callee = that.store; param = imm; break; case 5: //GOTO callee = that.goto; param = imm; break; case 6: //GTZ callee = that.gotoZero; param = imm; break; case 7: //GTP callee = that.gotoGreater; param = imm; break; case 8: //READ / WRITE /* if (imm == 0) callee = that.read; else if (imm == 1) callee = that.write; try { text = text[imm]; } catch (e) { } */ callee = that.read; param = imm; break; case 9: //SET callee = that.write; //that.set; param = imm; break; case 0: //STOP callee = that.stop; param = true; break; } text = text.replace("{0}", imm); if (animation != ANIMATE_NONE) { //cpuChip.setInstructionLabel(text); setStatus("פקודה נוכחית: " + text.replace(/
/g, ' ').replace(/\ \;/g, ' ')); } if (autoExecute) that.execute(callee, param); else { nextStep = that.execute; runNext(callee, param, single); } } /** * Performs EXECUTE operation */ this.execute = function(callee, param) { if (callee) { enable(false); callee(param); if (controls) controls.setFetch(); } } /** * Function to call when finished executing an instruction */ function executeDone() { if (!isRunning) enable(true); highlightLowMemory(cpuChip.getCellAt(2).getValue()); nextStep = that.fetch; if (_oldAnimation) that.setAnimation(_oldAnimation); runNext(); } /** * Automatically runs the program if on "run" mode */ function runNext(callee, param, single) { function _runNext() { if (nextStep) nextStep(callee, param); } if (isRunning || single) { if (animation == ANIMATE_DATA || animation == ANIMATE_FLOW) { setTimeout(_runNext, PROGRAM_FLOW_SPEED_COEFFICIENT * (1 / slider.getValue())); } else _runNext(); } } _startTime = null; /** * Starts fetching and exeuting automatically according to the selected animation type */ this.run = function() { isRunning = true; _startTime = new Date(); if (nextStep == that.execute) nextStep = that.decode; if (!nextStep) nextStep = that.fetch; runNext(); } /** * Receives numeric source code (rows separated with an '|') and loads it into the * instruction memory. */ this.loadProgram = function(code) { //Prepare code for splitting - remove whitespaces and comments code = '|' + code + '|'; code = code.replace(/\s/g, ""); code = code.replace(/\/\*([^*]|[\|]|(\*+([^*/]|[\|])))*\*+\//gm, ""). replace(/(\/\/)([^\|]*)(\|)/gm,"|"); code = code.replace(/\|{2,}/g,"|").replace(/^\|/,"").replace(/\|$/,""); var rows = code.split("|"); //Check code validity var validRows = 0; for (var i = 0; i < rows.length; i++) { numcheck = /\d{1,3}/; if (!numcheck.test(rows[i])) { if (rows[i] != "") { //Invalid code setStatus(MSG_INVALID_CODE, "#FF0000"); return; } } else validRows++; } if (validRows > 100) { setStatus(MSG_PROGRAM_TOO_LARGE, "#FF0000"); return; } //Read code to memory memoryChip.reset(); for (var i = 0; i < rows.length; i++) { if (rows[i] != "") { var value = parseInt(rows[i]); memoryChip.store(i, value); } } setStatus(MSG_PROGRAM_LOADED, null); that.reset(); } /** * Sets slider speed control for the computer */ this.setControls = function(obj) { slider = obj.slider; controls = obj; } /** * Shows latest values of all data cells in system * (To be used after running on "no animation") */ function showValues() { if (ioChip) ioChip.showValues(); if (cpuChip) cpuChip.showValues(); if (memoryChip) memoryChip.showValues(); } /** * Clears a buffer and resets it */ this.clearBuffers = function(opcode) { if (opcode != CLEAR_IO && opcode != CLEAR_MEMORY && opcode != CLEAR_HIGH_MEMORY && opcode != CLEAR_ALL) return; if (opcode == CLEAR_IO || opcode == CLEAR_ALL) ioChip.clear(); if (opcode == CLEAR_MEMORY || opcode == CLEAR_ALL) memoryChip.reset(); if (opcode == CLEAR_HIGH_MEMORY || opcode == CLEAR_ALL) memoryChip.clearHighMemory(); that.reset(); } //On load, reset computer this.reset(); }