diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea3ac3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.gitignore +.idea +.DS_Store +node_modules/* +ASMSimulator.iml \ No newline at end of file diff --git a/assets/asmsimulator.js b/assets/asmsimulator.js index 0590484..4aae1f4 100644 --- a/assets/asmsimulator.js +++ b/assets/asmsimulator.js @@ -21,6 +21,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes var mapping = {}; // Hash map of label used to replace the labels after the assembler generated the code var labels = {}; + // Hash of uppercase labels used to detect duplicates + var normalizedLabels = {}; var lines = input.split('\n'); // Split text into code lines @@ -136,11 +138,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes }; // Allowed: Label var parseLabel = function(input) { - if (regexLabel.exec(input)) { - return input.toUpperCase(); - } else { - return undefined; - } + return regexLabel.exec(input) ? input : undefined; }; var getValue = function(input) { switch(input.slice(0,1)) { @@ -167,12 +165,12 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes } }; var addLabel = function(label) { - label = label.toUpperCase(); - if (label in labels) + var upperLabel = label.toUpperCase(); + if (upperLabel in normalizedLabels) throw "Duplicate label: " + label; - if (label === "A" || label === "B" || label === "C" || label === "D") - throw "Label contains keyword: " + label; + if (upperLabel === "A" || upperLabel === "B" || upperLabel === "C" || upperLabel === "D") + throw "Label contains keyword: " + upperLabel; labels[label] = code.length; }; @@ -652,9 +650,9 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes self.gpr[reg] = value; } else if(reg == self.gpr.length) { self.sp=value; - if (self.sp < 0) { // Not likely to happen, since we always get here after checkOpertion(). + if (self.sp < self.minSP) { // Not likely to happen, since we always get here after checkOpertion(). throw "Stack overflow"; - } else if (self.sp > 231) { + } else if (self.sp > self.maxSP) { throw "Stack underflow"; } } else { @@ -713,13 +711,13 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes }; var push = function(value) { memory.store(self.sp--, value); - if (self.sp < 0) { + if (self.sp < self.minSP) { throw "Stack overflow"; } }; var pop = function() { var value = memory.load(++self.sp); - if (self.sp > 231) { + if (self.sp > self.maxSP) { throw "Stack underflow"; } @@ -1191,9 +1189,11 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes }, reset: function() { var self = this; + self.maxSP = 231; + self.minSP = 0; self.gpr = [0, 0, 0, 0]; - self.sp = 231; + self.sp = self.maxSP; self.ip = 0; self.zero = false; self.carry = false; @@ -1318,15 +1318,20 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes }; return opcodes; -}]);;app.controller('Ctrl', ['$scope', '$timeout', 'cpu', 'memory', 'assembler', function($scope, $timeout, cpu, memory, assembler) { +}]);;app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'assembler', function($document, $scope, $timeout, cpu, memory, assembler) { $scope.memory = memory; $scope.cpu = cpu; $scope.error = ''; $scope.isRunning = false; $scope.displayHex = true; $scope.displayInstr = true; + $scope.displayA = false; + $scope.displayB = false; + $scope.displayC = false; + $scope.displayD = false; $scope.speeds = [{speed:1, desc:"1 HZ"}, {speed:4, desc:"4 HZ"}, {speed:8, desc:"8 HZ"}, {speed:16, desc:"16 HZ"}]; $scope.speed = 4; + $scope.outputStartIndex = 232; $scope.code = "; Simple example\n; Writes Hello World to the output\n\n JMP start\nhello: DB \"Hello World!\" ; Variable\n DB 0 ; String terminator\n\nstart:\n MOV C, hello ; Point to var \n MOV D, 232 ; Point to output\n CALL print\n HLT ; Stop execution\n\nprint: ; print(C:*from, D:*to)\n PUSH A\n PUSH B\n MOV B, 0\n.loop:\n MOV A, [C] ; Get char from var\n MOV [D], A ; Write to output\n INC C\n INC D \n CMP B, [C] ; Check if end\n JNZ .loop ; jump if not\n\n POP B\n POP A\n RET"; @@ -1423,6 +1428,48 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes } } }; + + $scope.jumpToLine = function(index) { + $document[0].getElementById('sourceCode').scrollIntoView(); + $scope.selectedLine = $scope.mapping[index]; + }; + + + $scope.isInstruction = function(index) { + return $scope.mapping !== undefined && + $scope.mapping[index] !== undefined && + $scope.displayInstr; + }; + + $scope.getMemoryCellCss = function(index) { + if (index >= $scope.outputStartIndex) { + return 'output-bg'; + } else if ($scope.isInstruction(index)) { + return 'instr-bg'; + } else if (index > cpu.sp && index <= cpu.maxSP) { + return 'stack-bg'; + } else { + return ''; + } + }; + + $scope.getMemoryInnerCellCss = function(index) { + if (index === cpu.ip) { + return 'marker marker-ip'; + } else if (index === cpu.sp) { + return 'marker marker-sp'; + } else if (index === cpu.gpr[0] && $scope.displayA) { + return 'marker marker-a'; + } else if (index === cpu.gpr[1] && $scope.displayB) { + return 'marker marker-b'; + } else if (index === cpu.gpr[2] && $scope.displayC) { + return 'marker marker-c'; + } else if (index === cpu.gpr[3] && $scope.displayD) { + return 'marker marker-d'; + } else { + return ''; + } + }; }]);;app.filter('flag', function() { return function(input) { return input.toString().toUpperCase(); diff --git a/assets/asmsimulator.min.js b/assets/asmsimulator.min.js index a75c108..8324b7c 100644 --- a/assets/asmsimulator.min.js +++ b/assets/asmsimulator.min.js @@ -1,2 +1,2 @@ -/*! asmsimulator 23-01-2015 */ -var app=angular.module("ASMSimulator",[]);app.service("assembler",["opcodes",function(a){return{go:function(b){for(var c=/^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[(\w+((\+|-)\d+)?)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[(\w+((\+|-)\d+)?)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/,d=3,e=7,f=/^[-+]?[0-9]+$/,g=/^[.A-Za-z]\w*$/,h=[],i={},j={},k=b.split("\n"),l=function(a){if("0x"===a.slice(0,2))return parseInt(a.slice(2),16);if("0o"===a.slice(0,2))return parseInt(a.slice(2),8);if("b"===a.slice(a.length-1))return parseInt(a.slice(0,a.length-1),2);if("d"===a.slice(a.length-1))return parseInt(a.slice(0,a.length-1),10);if(f.exec(a))return parseInt(a,10);throw"Invalid number format"},m=function(a){return a=a.toUpperCase(),"A"===a?0:"B"===a?1:"C"===a?2:"D"===a?3:"SP"===a?4:void 0},n=function(a){a=a.toUpperCase();var b=0,c=0;if("A"===a[0])c=0;else if("B"===a[0])c=1;else if("C"===a[0])c=2;else if("D"===a[0])c=3;else{if("SP"!==a.slice(0,2))return void 0;c=4}var d=1;if(4===c&&(d=2),"-"===a[d])b=-1;else{if("+"!==a[d])return void 0;b=1}var e=b*parseInt(a.slice(d+1),10);if(-16>e||e>15)throw"offset must be a value between -16...+15";return 0>e&&(e=32+e),8*e+c},o=function(a,b,c){var d=m(a);if(void 0!==d)return{type:b,value:d};var e=p(a);if(void 0!==e)return{type:c,value:e};if("regaddress"===b&&(d=n(a),void 0!==d))return{type:b,value:d};var f=l(a);if(isNaN(f))throw"Not a "+c+": "+f;if(0>f||f>255)throw c+" must have a value between 0-255";return{type:c,value:f}},p=function(a){return g.exec(a)?a.toUpperCase():void 0},q=function(a){switch(a.slice(0,1)){case"[":var b=a.slice(1,a.length-1);return o(b,"regaddress","address");case'"':for(var c=a.slice(1,a.length-1),d=[],e=0,f=c.length;f>e;e++)d.push(c.charCodeAt(e));return{type:"numbers",value:d};case"'":var g=a.slice(1,a.length-1);if(g.length>1)throw"Only one character is allowed. Use String instead";return{type:"number",value:g.charCodeAt(0)};default:return o(a,"register","number")}},r=(function(a){if(a=a.toUpperCase(),a in j)throw"Duplicate label: "+a;if("A"===a||"B"===a||"C"===a||"D"===a)throw"Label contains keyword: "+a;j[a]=h.length}),s=function(a,b){if(void 0!==b)throw a+": too many arguments"},t=0,u=k.length;u>t;t++)try{var v=c.exec(k[t]);if(void 0!==v[1]||void 0!==v[2]){if(void 0!==v[1]&&r(v[1]),void 0!==v[2]){var w,x,y,z=v[2].toUpperCase();switch("DB"!==z&&(i[h.length]=t),z){case"DB":if(w=q(v[d]),"number"===w.type)h.push(w.value);else{if("numbers"!==w.type)throw"DB does not support this operand";for(var A=0,B=w.value.length;B>A;A++)h.push(w.value[A])}break;case"HLT":s("HLT",v[d]),y=a.NONE,h.push(y);break;case"MOV":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.MOV_REG_TO_REG;else if("register"===w.type&&"address"===x.type)y=a.MOV_ADDRESS_TO_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.MOV_REGADDRESS_TO_REG;else if("address"===w.type&&"register"===x.type)y=a.MOV_REG_TO_ADDRESS;else if("regaddress"===w.type&&"register"===x.type)y=a.MOV_REG_TO_REGADDRESS;else if("register"===w.type&&"number"===x.type)y=a.MOV_NUMBER_TO_REG;else if("address"===w.type&&"number"===x.type)y=a.MOV_NUMBER_TO_ADDRESS;else{if("regaddress"!==w.type||"number"!==x.type)throw"MOV does not support this operands";y=a.MOV_NUMBER_TO_REGADDRESS}h.push(y,w.value,x.value);break;case"ADD":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.ADD_REG_TO_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.ADD_REGADDRESS_TO_REG;else if("register"===w.type&&"address"===x.type)y=a.ADD_ADDRESS_TO_REG;else{if("register"!==w.type||"number"!==x.type)throw"ADD does not support this operands";y=a.ADD_NUMBER_TO_REG}h.push(y,w.value,x.value);break;case"SUB":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.SUB_REG_FROM_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.SUB_REGADDRESS_FROM_REG;else if("register"===w.type&&"address"===x.type)y=a.SUB_ADDRESS_FROM_REG;else{if("register"!==w.type||"number"!==x.type)throw"SUB does not support this operands";y=a.SUB_NUMBER_FROM_REG}h.push(y,w.value,x.value);break;case"INC":if(w=q(v[d]),s("INC",v[e]),"register"!==w.type)throw"INC does not support this operand";y=a.INC_REG,h.push(y,w.value);break;case"DEC":if(w=q(v[d]),s("DEC",v[e]),"register"!==w.type)throw"DEC does not support this operand";y=a.DEC_REG,h.push(y,w.value);break;case"CMP":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.CMP_REG_WITH_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.CMP_REGADDRESS_WITH_REG;else if("register"===w.type&&"address"===x.type)y=a.CMP_ADDRESS_WITH_REG;else{if("register"!==w.type||"number"!==x.type)throw"CMP does not support this operands";y=a.CMP_NUMBER_WITH_REG}h.push(y,w.value,x.value);break;case"JMP":if(w=q(v[d]),s("JMP",v[e]),"register"===w.type)y=a.JMP_REGADDRESS;else{if("number"!==w.type)throw"JMP does not support this operands";y=a.JMP_ADDRESS}h.push(y,w.value);break;case"JC":case"JB":case"JNAE":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.JC_REGADDRESS;else{if("number"!==w.type)throw z+" does not support this operand";y=a.JC_ADDRESS}h.push(y,w.value);break;case"JNC":case"JNB":case"JAE":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.JNC_REGADDRESS;else{if("number"!==w.type)throw z+"does not support this operand";y=a.JNC_ADDRESS}h.push(y,w.value);break;case"JZ":case"JE":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.JZ_REGADDRESS;else{if("number"!==w.type)throw z+" does not support this operand";y=a.JZ_ADDRESS}h.push(y,w.value);break;case"JNZ":case"JNE":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.JNZ_REGADDRESS;else{if("number"!==w.type)throw z+" does not support this operand";y=a.JNZ_ADDRESS}h.push(y,w.value);break;case"JA":case"JNBE":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.JA_REGADDRESS;else{if("number"!==w.type)throw z+" does not support this operand";y=a.JA_ADDRESS}h.push(y,w.value);break;case"JNA":case"JBE":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.JNA_REGADDRESS;else{if("number"!==w.type)throw z+" does not support this operand";y=a.JNA_ADDRESS}h.push(y,w.value);break;case"PUSH":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.PUSH_REG;else if("regaddress"===w.type)y=a.PUSH_REGADDRESS;else if("address"===w.type)y=a.PUSH_ADDRESS;else{if("number"!==w.type)throw"PUSH does not support this operand";y=a.PUSH_NUMBER}h.push(y,w.value);break;case"POP":if(w=q(v[d]),s(z,v[e]),"register"!==w.type)throw"PUSH does not support this operand";y=a.POP_REG,h.push(y,w.value);break;case"CALL":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.CALL_REGADDRESS;else{if("number"!==w.type)throw"CALL does not support this operand";y=a.CALL_ADDRESS}h.push(y,w.value);break;case"RET":s(z,v[d]),y=a.RET,h.push(y);break;case"MUL":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.MUL_REG;else if("regaddress"===w.type)y=a.MUL_REGADDRESS;else if("address"===w.type)y=a.MUL_ADDRESS;else{if("number"!==w.type)throw"MULL does not support this operand";y=a.MUL_NUMBER}h.push(y,w.value);break;case"DIV":if(w=q(v[d]),s(z,v[e]),"register"===w.type)y=a.DIV_REG;else if("regaddress"===w.type)y=a.DIV_REGADDRESS;else if("address"===w.type)y=a.DIV_ADDRESS;else{if("number"!==w.type)throw"DIV does not support this operand";y=a.DIV_NUMBER}h.push(y,w.value);break;case"AND":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.AND_REG_WITH_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.AND_REGADDRESS_WITH_REG;else if("register"===w.type&&"address"===x.type)y=a.AND_ADDRESS_WITH_REG;else{if("register"!==w.type||"number"!==x.type)throw"AND does not support this operands";y=a.AND_NUMBER_WITH_REG}h.push(y,w.value,x.value);break;case"OR":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.OR_REG_WITH_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.OR_REGADDRESS_WITH_REG;else if("register"===w.type&&"address"===x.type)y=a.OR_ADDRESS_WITH_REG;else{if("register"!==w.type||"number"!==x.type)throw"OR does not support this operands";y=a.OR_NUMBER_WITH_REG}h.push(y,w.value,x.value);break;case"XOR":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.XOR_REG_WITH_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.XOR_REGADDRESS_WITH_REG;else if("register"===w.type&&"address"===x.type)y=a.XOR_ADDRESS_WITH_REG;else{if("register"!==w.type||"number"!==x.type)throw"XOR does not support this operands";y=a.XOR_NUMBER_WITH_REG}h.push(y,w.value,x.value);break;case"NOT":if(w=q(v[d]),s(z,v[e]),"register"!==w.type)throw"NOT does not support this operand";y=a.NOT_REG,h.push(y,w.value);break;case"SHL":case"SAL":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.SHL_REG_WITH_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.SHL_REGADDRESS_WITH_REG;else if("register"===w.type&&"address"===x.type)y=a.SHL_ADDRESS_WITH_REG;else{if("register"!==w.type||"number"!==x.type)throw z+" does not support this operands";y=a.SHL_NUMBER_WITH_REG}h.push(y,w.value,x.value);break;case"SHR":case"SAR":if(w=q(v[d]),x=q(v[e]),"register"===w.type&&"register"===x.type)y=a.SHR_REG_WITH_REG;else if("register"===w.type&&"regaddress"===x.type)y=a.SHR_REGADDRESS_WITH_REG;else if("register"===w.type&&"address"===x.type)y=a.SHR_ADDRESS_WITH_REG;else{if("register"!==w.type||"number"!==x.type)throw z+" does not support this operands";y=a.SHR_NUMBER_WITH_REG}h.push(y,w.value,x.value);break;default:throw"Invalid instruction: "+v[2]}}}else{var C=k[t].trim();if(""!==C&&";"!==C.slice(0,1))throw"Syntax error"}}catch(D){throw{error:D,line:t}}for(t=0,u=h.length;u>t;t++)if(!angular.isNumber(h[t])){if(!(h[t]in j))throw{error:"Undefined label: "+h[t]};h[t]=j[h[t]]}return{code:h,mapping:i,labels:j}}}}]),app.service("cpu",["opcodes","memory",function(a,b){var c={step:function(){var c=this;if(c.fault===!0)throw"FAULT. Reset to continue.";try{var d=function(a){if(0>a||a>=c.gpr.length)throw"Invalid register: "+a;return a},e=function(a){if(0>a||a>=1+c.gpr.length)throw"Invalid register: "+a;return a},f=function(a,b){if(a>=0&&a231)throw"Stack underflow"}},g=function(a){if(a>=0&&a15&&(e-=32),b+e},i=function(a){return c.zero=!1,c.carry=!1,a>=256?(c.carry=!0,a%=256):0===a?c.zero=!0:0>a&&(c.carry=!0,a=255- -a%256),a},j=function(a){if(0>a||a>=b.data.length)throw"IP outside memory";c.ip=a},k=function(a){if(b.store(c.sp--,a),c.sp<0)throw"Stack overflow"},l=function(){var a=b.load(++c.sp);if(c.sp>231)throw"Stack underflow";return a},m=function(a){if(0===a)throw"Division by 0";return Math.floor(c.gpr[0]/a)};if(c.ip<0||c.ip>=b.data.length)throw"Instruction pointer is outside of memory";var n,o,p,q,r,s=b.load(c.ip);switch(s){case a.NONE:return!1;case a.MOV_REG_TO_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),f(n,g(o)),c.ip++;break;case a.MOV_ADDRESS_TO_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),f(n,b.load(p)),c.ip++;break;case a.MOV_REGADDRESS_TO_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),f(n,b.load(h(o))),c.ip++;break;case a.MOV_REG_TO_ADDRESS:q=b.load(++c.ip),o=e(b.load(++c.ip)),b.store(q,g(o)),c.ip++;break;case a.MOV_REG_TO_REGADDRESS:n=b.load(++c.ip),o=e(b.load(++c.ip)),b.store(h(n),g(o)),c.ip++;break;case a.MOV_NUMBER_TO_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),f(n,r),c.ip++;break;case a.MOV_NUMBER_TO_ADDRESS:q=b.load(++c.ip),r=b.load(++c.ip),b.store(q,r),c.ip++;break;case a.MOV_NUMBER_TO_REGADDRESS:n=b.load(++c.ip),r=b.load(++c.ip),b.store(h(n),r),c.ip++;break;case a.ADD_REG_TO_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),f(n,i(g(n)+g(o))),c.ip++;break;case a.ADD_REGADDRESS_TO_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),f(n,i(g(n)+b.load(h(o)))),c.ip++;break;case a.ADD_ADDRESS_TO_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),f(n,i(g(n)+b.load(p))),c.ip++;break;case a.ADD_NUMBER_TO_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),f(n,i(g(n)+r)),c.ip++;break;case a.SUB_REG_FROM_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),f(n,i(g(n)-c.gpr[o])),c.ip++;break;case a.SUB_REGADDRESS_FROM_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),f(n,i(g(n)-b.load(h(o)))),c.ip++;break;case a.SUB_ADDRESS_FROM_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),f(n,i(g(n)-b.load(p))),c.ip++;break;case a.SUB_NUMBER_FROM_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),f(n,i(g(n)-r)),c.ip++;break;case a.INC_REG:n=e(b.load(++c.ip)),f(n,i(g(n)+1)),c.ip++;break;case a.DEC_REG:n=e(b.load(++c.ip)),f(n,i(g(n)-1)),c.ip++;break;case a.CMP_REG_WITH_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),i(g(n)-g(o)),c.ip++;break;case a.CMP_REGADDRESS_WITH_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),i(g(n)-b.load(h(o))),c.ip++;break;case a.CMP_ADDRESS_WITH_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),i(g(n)-b.load(p)),c.ip++;break;case a.CMP_NUMBER_WITH_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),i(g(n)-r),c.ip++;break;case a.JMP_REGADDRESS:n=d(b.load(++c.ip)),j(c.gpr[n]);break;case a.JMP_ADDRESS:r=b.load(++c.ip),j(r);break;case a.JC_REGADDRESS:n=d(b.load(++c.ip)),c.carry?j(c.gpr[n]):c.ip++;break;case a.JC_ADDRESS:r=b.load(++c.ip),c.carry?j(r):c.ip++;break;case a.JNC_REGADDRESS:n=d(b.load(++c.ip)),c.carry?c.ip++:j(c.gpr[n]);break;case a.JNC_ADDRESS:r=b.load(++c.ip),c.carry?c.ip++:j(r);break;case a.JZ_REGADDRESS:n=d(b.load(++c.ip)),c.zero?j(c.gpr[n]):c.ip++;break;case a.JZ_ADDRESS:r=b.load(++c.ip),c.zero?j(r):c.ip++;break;case a.JNZ_REGADDRESS:n=d(b.load(++c.ip)),c.zero?c.ip++:j(c.gpr[n]);break;case a.JNZ_ADDRESS:r=b.load(++c.ip),c.zero?c.ip++:j(r);break;case a.JA_REGADDRESS:n=d(b.load(++c.ip)),c.zero||c.carry?c.ip++:j(c.gpr[n]);break;case a.JA_ADDRESS:r=b.load(++c.ip),c.zero||c.carry?c.ip++:j(r);break;case a.JNA_REGADDRESS:n=d(b.load(++c.ip)),c.zero||c.carry?j(c.gpr[n]):c.ip++;break;case a.JNA_ADDRESS:r=b.load(++c.ip),c.zero||c.carry?j(r):c.ip++;break;case a.PUSH_REG:o=d(b.load(++c.ip)),k(c.gpr[o]),c.ip++;break;case a.PUSH_REGADDRESS:o=b.load(++c.ip),k(b.load(h(o))),c.ip++;break;case a.PUSH_ADDRESS:p=b.load(++c.ip),k(b.load(p)),c.ip++;break;case a.PUSH_NUMBER:r=b.load(++c.ip),k(r),c.ip++;break;case a.POP_REG:n=d(b.load(++c.ip)),c.gpr[n]=l(),c.ip++;break;case a.CALL_REGADDRESS:n=d(b.load(++c.ip)),k(c.ip+1),j(c.gpr[n]);break;case a.CALL_ADDRESS:r=b.load(++c.ip),k(c.ip+1),j(r);break;case a.RET:j(l());break;case a.MUL_REG:o=d(b.load(++c.ip)),c.gpr[0]=i(c.gpr[0]*c.gpr[o]),c.ip++;break;case a.MUL_REGADDRESS:o=b.load(++c.ip),c.gpr[0]=i(c.gpr[0]*b.load(h(o))),c.ip++;break;case a.MUL_ADDRESS:p=b.load(++c.ip),c.gpr[0]=i(c.gpr[0]*b.load(p)),c.ip++;break;case a.MUL_NUMBER:r=b.load(++c.ip),c.gpr[0]=i(c.gpr[0]*r),c.ip++;break;case a.DIV_REG:o=d(b.load(++c.ip)),c.gpr[0]=i(m(c.gpr[o])),c.ip++;break;case a.DIV_REGADDRESS:o=b.load(++c.ip),c.gpr[0]=i(m(b.load(h(o)))),c.ip++;break;case a.DIV_ADDRESS:p=b.load(++c.ip),c.gpr[0]=i(m(b.load(p))),c.ip++;break;case a.DIV_NUMBER:r=b.load(++c.ip),c.gpr[0]=i(m(r)),c.ip++;break;case a.AND_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]&c.gpr[o]),c.ip++;break;case a.AND_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]&b.load(h(o))),c.ip++;break;case a.AND_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]&b.load(p)),c.ip++;break;case a.AND_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]&r),c.ip++;break;case a.OR_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]|c.gpr[o]),c.ip++;break;case a.OR_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]|b.load(h(o))),c.ip++;break;case a.OR_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]|b.load(p)),c.ip++;break;case a.OR_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]|r),c.ip++;break;case a.XOR_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]^c.gpr[o]),c.ip++;break;case a.XOR_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]^b.load(h(o))),c.ip++;break;case a.XOR_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]^b.load(p)),c.ip++;break;case a.XOR_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]^r),c.ip++;break;case a.NOT_REG:n=d(b.load(++c.ip)),c.gpr[n]=i(~c.gpr[n]),c.ip++;break;case a.SHL_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]<>>c.gpr[o]),c.ip++;break;case a.SHR_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]>>>b.load(h(o))),c.ip++;break;case a.SHR_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]>>>b.load(p)),c.ip++;break;case a.SHR_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]>>>r),c.ip++;break;default:throw"Invalid op code: "+s}return!0}catch(t){throw c.fault=!0,t}},reset:function(){var a=this;a.gpr=[0,0,0,0],a.sp=231,a.ip=0,a.zero=!1,a.carry=!1,a.fault=!1}};return c.reset(),c}]),app.service("memory",[function(){var a={data:Array(256),lastAccess:-1,load:function(a){var b=this;if(0>a||a>=b.data.length)throw"Memory access violation at "+a;return b.lastAccess=a,b.data[a]},store:function(a,b){var c=this;if(0>a||a>=c.data.length)throw"Memory access violation at "+a;c.lastAccess=a,c.data[a]=b},reset:function(){var a=this;a.lastAccess=-1;for(var b=0,c=a.data.length;c>b;b++)a.data[b]=0}};return a.reset(),a}]),app.service("opcodes",[function(){var a={NONE:0,MOV_REG_TO_REG:1,MOV_ADDRESS_TO_REG:2,MOV_REGADDRESS_TO_REG:3,MOV_REG_TO_ADDRESS:4,MOV_REG_TO_REGADDRESS:5,MOV_NUMBER_TO_REG:6,MOV_NUMBER_TO_ADDRESS:7,MOV_NUMBER_TO_REGADDRESS:8,ADD_REG_TO_REG:10,ADD_REGADDRESS_TO_REG:11,ADD_ADDRESS_TO_REG:12,ADD_NUMBER_TO_REG:13,SUB_REG_FROM_REG:14,SUB_REGADDRESS_FROM_REG:15,SUB_ADDRESS_FROM_REG:16,SUB_NUMBER_FROM_REG:17,INC_REG:18,DEC_REG:19,CMP_REG_WITH_REG:20,CMP_REGADDRESS_WITH_REG:21,CMP_ADDRESS_WITH_REG:22,CMP_NUMBER_WITH_REG:23,JMP_REGADDRESS:30,JMP_ADDRESS:31,JC_REGADDRESS:32,JC_ADDRESS:33,JNC_REGADDRESS:34,JNC_ADDRESS:35,JZ_REGADDRESS:36,JZ_ADDRESS:37,JNZ_REGADDRESS:38,JNZ_ADDRESS:39,JA_REGADDRESS:40,JA_ADDRESS:41,JNA_REGADDRESS:42,JNA_ADDRESS:43,PUSH_REG:50,PUSH_REGADDRESS:51,PUSH_ADDRESS:52,PUSH_NUMBER:53,POP_REG:54,CALL_REGADDRESS:55,CALL_ADDRESS:56,RET:57,MUL_REG:60,MUL_REGADDRESS:61,MUL_ADDRESS:62,MUL_NUMBER:63,DIV_REG:64,DIV_REGADDRESS:65,DIV_ADDRESS:66,DIV_NUMBER:67,AND_REG_WITH_REG:70,AND_REGADDRESS_WITH_REG:71,AND_ADDRESS_WITH_REG:72,AND_NUMBER_WITH_REG:73,OR_REG_WITH_REG:74,OR_REGADDRESS_WITH_REG:75,OR_ADDRESS_WITH_REG:76,OR_NUMBER_WITH_REG:77,XOR_REG_WITH_REG:78,XOR_REGADDRESS_WITH_REG:79,XOR_ADDRESS_WITH_REG:80,XOR_NUMBER_WITH_REG:81,NOT_REG:82,SHL_REG_WITH_REG:90,SHL_REGADDRESS_WITH_REG:91,SHL_ADDRESS_WITH_REG:92,SHL_NUMBER_WITH_REG:93,SHR_REG_WITH_REG:94,SHR_REGADDRESS_WITH_REG:95,SHR_ADDRESS_WITH_REG:96,SHR_NUMBER_WITH_REG:97};return a}]),app.controller("Ctrl",["$scope","$timeout","cpu","memory","assembler",function(a,b,c,d,e){a.memory=d,a.cpu=c,a.error="",a.isRunning=!1,a.displayHex=!0,a.displayInstr=!0,a.speeds=[{speed:1,desc:"1 HZ"},{speed:4,desc:"4 HZ"},{speed:8,desc:"8 HZ"},{speed:16,desc:"16 HZ"}],a.speed=4,a.code='; Simple example\n; Writes Hello World to the output\n\n JMP start\nhello: DB "Hello World!" ; Variable\n DB 0 ; String terminator\n\nstart:\n MOV C, hello ; Point to var \n MOV D, 232 ; Point to output\n CALL print\n HLT ; Stop execution\n\nprint: ; print(C:*from, D:*to)\n PUSH A\n PUSH B\n MOV B, 0\n.loop:\n MOV A, [C] ; Get char from var\n MOV [D], A ; Write to output\n INC C\n INC D \n CMP B, [C] ; Check if end\n JNZ .loop ; jump if not\n\n POP B\n POP A\n RET',a.reset=function(){c.reset(),d.reset(),a.error="",a.selectedLine=-1},a.executeStep=function(){a.checkPrgrmLoaded()||a.assemble();try{var b=c.step();return c.ip in a.mapping&&(a.selectedLine=a.mapping[c.ip]),b}catch(d){return a.error=d,!1}};var f;a.run=function(){a.checkPrgrmLoaded()||a.assemble(),a.isRunning=!0,f=b(function(){a.executeStep()===!0?a.run():a.isRunning=!1},1e3/a.speed)},a.stop=function(){b.cancel(f),a.isRunning=!1},a.checkPrgrmLoaded=function(){for(var a=0,b=d.data.length;b>a;a++)if(0!==d.data[a])return!0;return!1},a.getChar=function(a){var b=String.fromCharCode(a);return""===b.trim()?"  ":b},a.assemble=function(){try{a.reset();var b=e.go(a.code);a.mapping=b.mapping;var c=b.code;if(a.labels=b.labels,c.length>d.data.length)throw"Binary code does not fit into the memory. Max "+d.data.length+" bytes are allowed";for(var f=0,g=c.length;g>f;f++)d.data[f]=c[f]}catch(h){void 0!==h.line?(a.error=h.line+" | "+h.error,a.selectedLine=h.line):a.error=h.error}}}]),app.filter("flag",function(){return function(a){return a.toString().toUpperCase()}}),app.filter("number",function(){return function(a,b){if(b){var c=a.toString(16).toUpperCase();return 1==c.length?"0"+c:c}return a.toString(10)}}),app.directive("selectLine",[function(){return{restrict:"A",link:function(a,b){a.$watch("selectedLine",function(){if(a.selectedLine>=0){for(var c=b[0].value.split("\n"),d=0,e=0;ee||e>15)throw"offset must be a value between -16...+15";return 0>e&&(e=32+e),8*e+c},p=function(a,b,c){var d=n(a);if(void 0!==d)return{type:b,value:d};var e=q(a);if(void 0!==e)return{type:c,value:e};if("regaddress"===b&&(d=o(a),void 0!==d))return{type:b,value:d};var f=m(a);if(isNaN(f))throw"Not a "+c+": "+f;if(0>f||f>255)throw c+" must have a value between 0-255";return{type:c,value:f}},q=function(a){return g.exec(a)?a:void 0},r=function(a){switch(a.slice(0,1)){case"[":var b=a.slice(1,a.length-1);return p(b,"regaddress","address");case'"':for(var c=a.slice(1,a.length-1),d=[],e=0,f=c.length;f>e;e++)d.push(c.charCodeAt(e));return{type:"numbers",value:d};case"'":var g=a.slice(1,a.length-1);if(g.length>1)throw"Only one character is allowed. Use String instead";return{type:"number",value:g.charCodeAt(0)};default:return p(a,"register","number")}},s=(function(a){var b=a.toUpperCase();if(b in k)throw"Duplicate label: "+a;if("A"===b||"B"===b||"C"===b||"D"===b)throw"Label contains keyword: "+b;j[a]=h.length}),t=function(a,b){if(void 0!==b)throw a+": too many arguments"},u=0,v=l.length;v>u;u++)try{var w=c.exec(l[u]);if(void 0!==w[1]||void 0!==w[2]){if(void 0!==w[1]&&s(w[1]),void 0!==w[2]){var x,y,z,A=w[2].toUpperCase();switch("DB"!==A&&(i[h.length]=u),A){case"DB":if(x=r(w[d]),"number"===x.type)h.push(x.value);else{if("numbers"!==x.type)throw"DB does not support this operand";for(var B=0,C=x.value.length;C>B;B++)h.push(x.value[B])}break;case"HLT":t("HLT",w[d]),z=a.NONE,h.push(z);break;case"MOV":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.MOV_REG_TO_REG;else if("register"===x.type&&"address"===y.type)z=a.MOV_ADDRESS_TO_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.MOV_REGADDRESS_TO_REG;else if("address"===x.type&&"register"===y.type)z=a.MOV_REG_TO_ADDRESS;else if("regaddress"===x.type&&"register"===y.type)z=a.MOV_REG_TO_REGADDRESS;else if("register"===x.type&&"number"===y.type)z=a.MOV_NUMBER_TO_REG;else if("address"===x.type&&"number"===y.type)z=a.MOV_NUMBER_TO_ADDRESS;else{if("regaddress"!==x.type||"number"!==y.type)throw"MOV does not support this operands";z=a.MOV_NUMBER_TO_REGADDRESS}h.push(z,x.value,y.value);break;case"ADD":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.ADD_REG_TO_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.ADD_REGADDRESS_TO_REG;else if("register"===x.type&&"address"===y.type)z=a.ADD_ADDRESS_TO_REG;else{if("register"!==x.type||"number"!==y.type)throw"ADD does not support this operands";z=a.ADD_NUMBER_TO_REG}h.push(z,x.value,y.value);break;case"SUB":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.SUB_REG_FROM_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.SUB_REGADDRESS_FROM_REG;else if("register"===x.type&&"address"===y.type)z=a.SUB_ADDRESS_FROM_REG;else{if("register"!==x.type||"number"!==y.type)throw"SUB does not support this operands";z=a.SUB_NUMBER_FROM_REG}h.push(z,x.value,y.value);break;case"INC":if(x=r(w[d]),t("INC",w[e]),"register"!==x.type)throw"INC does not support this operand";z=a.INC_REG,h.push(z,x.value);break;case"DEC":if(x=r(w[d]),t("DEC",w[e]),"register"!==x.type)throw"DEC does not support this operand";z=a.DEC_REG,h.push(z,x.value);break;case"CMP":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.CMP_REG_WITH_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.CMP_REGADDRESS_WITH_REG;else if("register"===x.type&&"address"===y.type)z=a.CMP_ADDRESS_WITH_REG;else{if("register"!==x.type||"number"!==y.type)throw"CMP does not support this operands";z=a.CMP_NUMBER_WITH_REG}h.push(z,x.value,y.value);break;case"JMP":if(x=r(w[d]),t("JMP",w[e]),"register"===x.type)z=a.JMP_REGADDRESS;else{if("number"!==x.type)throw"JMP does not support this operands";z=a.JMP_ADDRESS}h.push(z,x.value);break;case"JC":case"JB":case"JNAE":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.JC_REGADDRESS;else{if("number"!==x.type)throw A+" does not support this operand";z=a.JC_ADDRESS}h.push(z,x.value);break;case"JNC":case"JNB":case"JAE":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.JNC_REGADDRESS;else{if("number"!==x.type)throw A+"does not support this operand";z=a.JNC_ADDRESS}h.push(z,x.value);break;case"JZ":case"JE":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.JZ_REGADDRESS;else{if("number"!==x.type)throw A+" does not support this operand";z=a.JZ_ADDRESS}h.push(z,x.value);break;case"JNZ":case"JNE":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.JNZ_REGADDRESS;else{if("number"!==x.type)throw A+" does not support this operand";z=a.JNZ_ADDRESS}h.push(z,x.value);break;case"JA":case"JNBE":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.JA_REGADDRESS;else{if("number"!==x.type)throw A+" does not support this operand";z=a.JA_ADDRESS}h.push(z,x.value);break;case"JNA":case"JBE":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.JNA_REGADDRESS;else{if("number"!==x.type)throw A+" does not support this operand";z=a.JNA_ADDRESS}h.push(z,x.value);break;case"PUSH":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.PUSH_REG;else if("regaddress"===x.type)z=a.PUSH_REGADDRESS;else if("address"===x.type)z=a.PUSH_ADDRESS;else{if("number"!==x.type)throw"PUSH does not support this operand";z=a.PUSH_NUMBER}h.push(z,x.value);break;case"POP":if(x=r(w[d]),t(A,w[e]),"register"!==x.type)throw"PUSH does not support this operand";z=a.POP_REG,h.push(z,x.value);break;case"CALL":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.CALL_REGADDRESS;else{if("number"!==x.type)throw"CALL does not support this operand";z=a.CALL_ADDRESS}h.push(z,x.value);break;case"RET":t(A,w[d]),z=a.RET,h.push(z);break;case"MUL":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.MUL_REG;else if("regaddress"===x.type)z=a.MUL_REGADDRESS;else if("address"===x.type)z=a.MUL_ADDRESS;else{if("number"!==x.type)throw"MULL does not support this operand";z=a.MUL_NUMBER}h.push(z,x.value);break;case"DIV":if(x=r(w[d]),t(A,w[e]),"register"===x.type)z=a.DIV_REG;else if("regaddress"===x.type)z=a.DIV_REGADDRESS;else if("address"===x.type)z=a.DIV_ADDRESS;else{if("number"!==x.type)throw"DIV does not support this operand";z=a.DIV_NUMBER}h.push(z,x.value);break;case"AND":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.AND_REG_WITH_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.AND_REGADDRESS_WITH_REG;else if("register"===x.type&&"address"===y.type)z=a.AND_ADDRESS_WITH_REG;else{if("register"!==x.type||"number"!==y.type)throw"AND does not support this operands";z=a.AND_NUMBER_WITH_REG}h.push(z,x.value,y.value);break;case"OR":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.OR_REG_WITH_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.OR_REGADDRESS_WITH_REG;else if("register"===x.type&&"address"===y.type)z=a.OR_ADDRESS_WITH_REG;else{if("register"!==x.type||"number"!==y.type)throw"OR does not support this operands";z=a.OR_NUMBER_WITH_REG}h.push(z,x.value,y.value);break;case"XOR":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.XOR_REG_WITH_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.XOR_REGADDRESS_WITH_REG;else if("register"===x.type&&"address"===y.type)z=a.XOR_ADDRESS_WITH_REG;else{if("register"!==x.type||"number"!==y.type)throw"XOR does not support this operands";z=a.XOR_NUMBER_WITH_REG}h.push(z,x.value,y.value);break;case"NOT":if(x=r(w[d]),t(A,w[e]),"register"!==x.type)throw"NOT does not support this operand";z=a.NOT_REG,h.push(z,x.value);break;case"SHL":case"SAL":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.SHL_REG_WITH_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.SHL_REGADDRESS_WITH_REG;else if("register"===x.type&&"address"===y.type)z=a.SHL_ADDRESS_WITH_REG;else{if("register"!==x.type||"number"!==y.type)throw A+" does not support this operands";z=a.SHL_NUMBER_WITH_REG}h.push(z,x.value,y.value);break;case"SHR":case"SAR":if(x=r(w[d]),y=r(w[e]),"register"===x.type&&"register"===y.type)z=a.SHR_REG_WITH_REG;else if("register"===x.type&&"regaddress"===y.type)z=a.SHR_REGADDRESS_WITH_REG;else if("register"===x.type&&"address"===y.type)z=a.SHR_ADDRESS_WITH_REG;else{if("register"!==x.type||"number"!==y.type)throw A+" does not support this operands";z=a.SHR_NUMBER_WITH_REG}h.push(z,x.value,y.value);break;default:throw"Invalid instruction: "+w[2]}}}else{var D=l[u].trim();if(""!==D&&";"!==D.slice(0,1))throw"Syntax error"}}catch(E){throw{error:E,line:u}}for(u=0,v=h.length;v>u;u++)if(!angular.isNumber(h[u])){if(!(h[u]in j))throw{error:"Undefined label: "+h[u]};h[u]=j[h[u]]}return{code:h,mapping:i,labels:j}}}}]),app.service("cpu",["opcodes","memory",function(a,b){var c={step:function(){var c=this;if(c.fault===!0)throw"FAULT. Reset to continue.";try{var d=function(a){if(0>a||a>=c.gpr.length)throw"Invalid register: "+a;return a},e=function(a){if(0>a||a>=1+c.gpr.length)throw"Invalid register: "+a;return a},f=function(a,b){if(a>=0&&ac.maxSP)throw"Stack underflow"}},g=function(a){if(a>=0&&a15&&(e-=32),b+e},i=function(a){return c.zero=!1,c.carry=!1,a>=256?(c.carry=!0,a%=256):0===a?c.zero=!0:0>a&&(c.carry=!0,a=255- -a%256),a},j=function(a){if(0>a||a>=b.data.length)throw"IP outside memory";c.ip=a},k=function(a){if(b.store(c.sp--,a),c.spc.maxSP)throw"Stack underflow";return a},m=function(a){if(0===a)throw"Division by 0";return Math.floor(c.gpr[0]/a)};if(c.ip<0||c.ip>=b.data.length)throw"Instruction pointer is outside of memory";var n,o,p,q,r,s=b.load(c.ip);switch(s){case a.NONE:return!1;case a.MOV_REG_TO_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),f(n,g(o)),c.ip++;break;case a.MOV_ADDRESS_TO_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),f(n,b.load(p)),c.ip++;break;case a.MOV_REGADDRESS_TO_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),f(n,b.load(h(o))),c.ip++;break;case a.MOV_REG_TO_ADDRESS:q=b.load(++c.ip),o=e(b.load(++c.ip)),b.store(q,g(o)),c.ip++;break;case a.MOV_REG_TO_REGADDRESS:n=b.load(++c.ip),o=e(b.load(++c.ip)),b.store(h(n),g(o)),c.ip++;break;case a.MOV_NUMBER_TO_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),f(n,r),c.ip++;break;case a.MOV_NUMBER_TO_ADDRESS:q=b.load(++c.ip),r=b.load(++c.ip),b.store(q,r),c.ip++;break;case a.MOV_NUMBER_TO_REGADDRESS:n=b.load(++c.ip),r=b.load(++c.ip),b.store(h(n),r),c.ip++;break;case a.ADD_REG_TO_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),f(n,i(g(n)+g(o))),c.ip++;break;case a.ADD_REGADDRESS_TO_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),f(n,i(g(n)+b.load(h(o)))),c.ip++;break;case a.ADD_ADDRESS_TO_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),f(n,i(g(n)+b.load(p))),c.ip++;break;case a.ADD_NUMBER_TO_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),f(n,i(g(n)+r)),c.ip++;break;case a.SUB_REG_FROM_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),f(n,i(g(n)-c.gpr[o])),c.ip++;break;case a.SUB_REGADDRESS_FROM_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),f(n,i(g(n)-b.load(h(o)))),c.ip++;break;case a.SUB_ADDRESS_FROM_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),f(n,i(g(n)-b.load(p))),c.ip++;break;case a.SUB_NUMBER_FROM_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),f(n,i(g(n)-r)),c.ip++;break;case a.INC_REG:n=e(b.load(++c.ip)),f(n,i(g(n)+1)),c.ip++;break;case a.DEC_REG:n=e(b.load(++c.ip)),f(n,i(g(n)-1)),c.ip++;break;case a.CMP_REG_WITH_REG:n=e(b.load(++c.ip)),o=e(b.load(++c.ip)),i(g(n)-g(o)),c.ip++;break;case a.CMP_REGADDRESS_WITH_REG:n=e(b.load(++c.ip)),o=b.load(++c.ip),i(g(n)-b.load(h(o))),c.ip++;break;case a.CMP_ADDRESS_WITH_REG:n=e(b.load(++c.ip)),p=b.load(++c.ip),i(g(n)-b.load(p)),c.ip++;break;case a.CMP_NUMBER_WITH_REG:n=e(b.load(++c.ip)),r=b.load(++c.ip),i(g(n)-r),c.ip++;break;case a.JMP_REGADDRESS:n=d(b.load(++c.ip)),j(c.gpr[n]);break;case a.JMP_ADDRESS:r=b.load(++c.ip),j(r);break;case a.JC_REGADDRESS:n=d(b.load(++c.ip)),c.carry?j(c.gpr[n]):c.ip++;break;case a.JC_ADDRESS:r=b.load(++c.ip),c.carry?j(r):c.ip++;break;case a.JNC_REGADDRESS:n=d(b.load(++c.ip)),c.carry?c.ip++:j(c.gpr[n]);break;case a.JNC_ADDRESS:r=b.load(++c.ip),c.carry?c.ip++:j(r);break;case a.JZ_REGADDRESS:n=d(b.load(++c.ip)),c.zero?j(c.gpr[n]):c.ip++;break;case a.JZ_ADDRESS:r=b.load(++c.ip),c.zero?j(r):c.ip++;break;case a.JNZ_REGADDRESS:n=d(b.load(++c.ip)),c.zero?c.ip++:j(c.gpr[n]);break;case a.JNZ_ADDRESS:r=b.load(++c.ip),c.zero?c.ip++:j(r);break;case a.JA_REGADDRESS:n=d(b.load(++c.ip)),c.zero||c.carry?c.ip++:j(c.gpr[n]);break;case a.JA_ADDRESS:r=b.load(++c.ip),c.zero||c.carry?c.ip++:j(r);break;case a.JNA_REGADDRESS:n=d(b.load(++c.ip)),c.zero||c.carry?j(c.gpr[n]):c.ip++;break;case a.JNA_ADDRESS:r=b.load(++c.ip),c.zero||c.carry?j(r):c.ip++;break;case a.PUSH_REG:o=d(b.load(++c.ip)),k(c.gpr[o]),c.ip++;break;case a.PUSH_REGADDRESS:o=b.load(++c.ip),k(b.load(h(o))),c.ip++;break;case a.PUSH_ADDRESS:p=b.load(++c.ip),k(b.load(p)),c.ip++;break;case a.PUSH_NUMBER:r=b.load(++c.ip),k(r),c.ip++;break;case a.POP_REG:n=d(b.load(++c.ip)),c.gpr[n]=l(),c.ip++;break;case a.CALL_REGADDRESS:n=d(b.load(++c.ip)),k(c.ip+1),j(c.gpr[n]);break;case a.CALL_ADDRESS:r=b.load(++c.ip),k(c.ip+1),j(r);break;case a.RET:j(l());break;case a.MUL_REG:o=d(b.load(++c.ip)),c.gpr[0]=i(c.gpr[0]*c.gpr[o]),c.ip++;break;case a.MUL_REGADDRESS:o=b.load(++c.ip),c.gpr[0]=i(c.gpr[0]*b.load(h(o))),c.ip++;break;case a.MUL_ADDRESS:p=b.load(++c.ip),c.gpr[0]=i(c.gpr[0]*b.load(p)),c.ip++;break;case a.MUL_NUMBER:r=b.load(++c.ip),c.gpr[0]=i(c.gpr[0]*r),c.ip++;break;case a.DIV_REG:o=d(b.load(++c.ip)),c.gpr[0]=i(m(c.gpr[o])),c.ip++;break;case a.DIV_REGADDRESS:o=b.load(++c.ip),c.gpr[0]=i(m(b.load(h(o)))),c.ip++;break;case a.DIV_ADDRESS:p=b.load(++c.ip),c.gpr[0]=i(m(b.load(p))),c.ip++;break;case a.DIV_NUMBER:r=b.load(++c.ip),c.gpr[0]=i(m(r)),c.ip++;break;case a.AND_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]&c.gpr[o]),c.ip++;break;case a.AND_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]&b.load(h(o))),c.ip++;break;case a.AND_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]&b.load(p)),c.ip++;break;case a.AND_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]&r),c.ip++;break;case a.OR_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]|c.gpr[o]),c.ip++;break;case a.OR_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]|b.load(h(o))),c.ip++;break;case a.OR_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]|b.load(p)),c.ip++;break;case a.OR_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]|r),c.ip++;break;case a.XOR_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]^c.gpr[o]),c.ip++;break;case a.XOR_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]^b.load(h(o))),c.ip++;break;case a.XOR_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]^b.load(p)),c.ip++;break;case a.XOR_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]^r),c.ip++;break;case a.NOT_REG:n=d(b.load(++c.ip)),c.gpr[n]=i(~c.gpr[n]),c.ip++;break;case a.SHL_REG_WITH_REG:n=d(b.load(++c.ip)),o=d(b.load(++c.ip)),c.gpr[n]=i(c.gpr[n]<>>c.gpr[o]),c.ip++;break;case a.SHR_REGADDRESS_WITH_REG:n=d(b.load(++c.ip)),o=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]>>>b.load(h(o))),c.ip++;break;case a.SHR_ADDRESS_WITH_REG:n=d(b.load(++c.ip)),p=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]>>>b.load(p)),c.ip++;break;case a.SHR_NUMBER_WITH_REG:n=d(b.load(++c.ip)),r=b.load(++c.ip),c.gpr[n]=i(c.gpr[n]>>>r),c.ip++;break;default:throw"Invalid op code: "+s}return!0}catch(t){throw c.fault=!0,t}},reset:function(){var a=this;a.maxSP=231,a.minSP=0,a.gpr=[0,0,0,0],a.sp=a.maxSP,a.ip=0,a.zero=!1,a.carry=!1,a.fault=!1}};return c.reset(),c}]),app.service("memory",[function(){var a={data:Array(256),lastAccess:-1,load:function(a){var b=this;if(0>a||a>=b.data.length)throw"Memory access violation at "+a;return b.lastAccess=a,b.data[a]},store:function(a,b){var c=this;if(0>a||a>=c.data.length)throw"Memory access violation at "+a;c.lastAccess=a,c.data[a]=b},reset:function(){var a=this;a.lastAccess=-1;for(var b=0,c=a.data.length;c>b;b++)a.data[b]=0}};return a.reset(),a}]),app.service("opcodes",[function(){var a={NONE:0,MOV_REG_TO_REG:1,MOV_ADDRESS_TO_REG:2,MOV_REGADDRESS_TO_REG:3,MOV_REG_TO_ADDRESS:4,MOV_REG_TO_REGADDRESS:5,MOV_NUMBER_TO_REG:6,MOV_NUMBER_TO_ADDRESS:7,MOV_NUMBER_TO_REGADDRESS:8,ADD_REG_TO_REG:10,ADD_REGADDRESS_TO_REG:11,ADD_ADDRESS_TO_REG:12,ADD_NUMBER_TO_REG:13,SUB_REG_FROM_REG:14,SUB_REGADDRESS_FROM_REG:15,SUB_ADDRESS_FROM_REG:16,SUB_NUMBER_FROM_REG:17,INC_REG:18,DEC_REG:19,CMP_REG_WITH_REG:20,CMP_REGADDRESS_WITH_REG:21,CMP_ADDRESS_WITH_REG:22,CMP_NUMBER_WITH_REG:23,JMP_REGADDRESS:30,JMP_ADDRESS:31,JC_REGADDRESS:32,JC_ADDRESS:33,JNC_REGADDRESS:34,JNC_ADDRESS:35,JZ_REGADDRESS:36,JZ_ADDRESS:37,JNZ_REGADDRESS:38,JNZ_ADDRESS:39,JA_REGADDRESS:40,JA_ADDRESS:41,JNA_REGADDRESS:42,JNA_ADDRESS:43,PUSH_REG:50,PUSH_REGADDRESS:51,PUSH_ADDRESS:52,PUSH_NUMBER:53,POP_REG:54,CALL_REGADDRESS:55,CALL_ADDRESS:56,RET:57,MUL_REG:60,MUL_REGADDRESS:61,MUL_ADDRESS:62,MUL_NUMBER:63,DIV_REG:64,DIV_REGADDRESS:65,DIV_ADDRESS:66,DIV_NUMBER:67,AND_REG_WITH_REG:70,AND_REGADDRESS_WITH_REG:71,AND_ADDRESS_WITH_REG:72,AND_NUMBER_WITH_REG:73,OR_REG_WITH_REG:74,OR_REGADDRESS_WITH_REG:75,OR_ADDRESS_WITH_REG:76,OR_NUMBER_WITH_REG:77,XOR_REG_WITH_REG:78,XOR_REGADDRESS_WITH_REG:79,XOR_ADDRESS_WITH_REG:80,XOR_NUMBER_WITH_REG:81,NOT_REG:82,SHL_REG_WITH_REG:90,SHL_REGADDRESS_WITH_REG:91,SHL_ADDRESS_WITH_REG:92,SHL_NUMBER_WITH_REG:93,SHR_REG_WITH_REG:94,SHR_REGADDRESS_WITH_REG:95,SHR_ADDRESS_WITH_REG:96,SHR_NUMBER_WITH_REG:97};return a}]),app.controller("Ctrl",["$document","$scope","$timeout","cpu","memory","assembler",function(a,b,c,d,e,f){b.memory=e,b.cpu=d,b.error="",b.isRunning=!1,b.displayHex=!0,b.displayInstr=!0,b.displayA=!1,b.displayB=!1,b.displayC=!1,b.displayD=!1,b.speeds=[{speed:1,desc:"1 HZ"},{speed:4,desc:"4 HZ"},{speed:8,desc:"8 HZ"},{speed:16,desc:"16 HZ"}],b.speed=4,b.outputStartIndex=232,b.code='; Simple example\n; Writes Hello World to the output\n\n JMP start\nhello: DB "Hello World!" ; Variable\n DB 0 ; String terminator\n\nstart:\n MOV C, hello ; Point to var \n MOV D, 232 ; Point to output\n CALL print\n HLT ; Stop execution\n\nprint: ; print(C:*from, D:*to)\n PUSH A\n PUSH B\n MOV B, 0\n.loop:\n MOV A, [C] ; Get char from var\n MOV [D], A ; Write to output\n INC C\n INC D \n CMP B, [C] ; Check if end\n JNZ .loop ; jump if not\n\n POP B\n POP A\n RET',b.reset=function(){d.reset(),e.reset(),b.error="",b.selectedLine=-1},b.executeStep=function(){b.checkPrgrmLoaded()||b.assemble();try{var a=d.step();return d.ip in b.mapping&&(b.selectedLine=b.mapping[d.ip]),a}catch(c){return b.error=c,!1}};var g;b.run=function(){b.checkPrgrmLoaded()||b.assemble(),b.isRunning=!0,g=c(function(){b.executeStep()===!0?b.run():b.isRunning=!1},1e3/b.speed)},b.stop=function(){c.cancel(g),b.isRunning=!1},b.checkPrgrmLoaded=function(){for(var a=0,b=e.data.length;b>a;a++)if(0!==e.data[a])return!0;return!1},b.getChar=function(a){var b=String.fromCharCode(a);return""===b.trim()?"  ":b},b.assemble=function(){try{b.reset();var a=f.go(b.code);b.mapping=a.mapping;var c=a.code;if(b.labels=a.labels,c.length>e.data.length)throw"Binary code does not fit into the memory. Max "+e.data.length+" bytes are allowed";for(var d=0,g=c.length;g>d;d++)e.data[d]=c[d]}catch(h){void 0!==h.line?(b.error=h.line+" | "+h.error,b.selectedLine=h.line):b.error=h.error}},b.jumpToLine=function(c){a[0].getElementById("sourceCode").scrollIntoView(),b.selectedLine=b.mapping[c]},b.isInstruction=function(a){return void 0!==b.mapping&&void 0!==b.mapping[a]&&b.displayInstr},b.getMemoryCellCss=function(a){return a>=b.outputStartIndex?"output-bg":b.isInstruction(a)?"instr-bg":a>d.sp&&a<=d.maxSP?"stack-bg":""},b.getMemoryInnerCellCss=function(a){return a===d.ip?"marker marker-ip":a===d.sp?"marker marker-sp":a===d.gpr[0]&&b.displayA?"marker marker-a":a===d.gpr[1]&&b.displayB?"marker marker-b":a===d.gpr[2]&&b.displayC?"marker marker-c":a===d.gpr[3]&&b.displayD?"marker marker-d":""}}]),app.filter("flag",function(){return function(a){return a.toString().toUpperCase()}}),app.filter("number",function(){return function(a,b){if(b){var c=a.toString(16).toUpperCase();return 1==c.length?"0"+c:c}return a.toString(10)}}),app.directive("selectLine",[function(){return{restrict:"A",link:function(a,b){a.$watch("selectedLine",function(){if(a.selectedLine>=0){for(var c=b[0].value.split("\n"),d=0,e=0;e

diff --git a/package.json b/package.json index 46a0221..5dce944 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "asmsimulator", - "version": "0.4.0", + "version": "0.5.0", "description": "Simple 8-bit Assembler Simulator in Javascript", "author": "Marco Schweighauser", "license": "MIT", diff --git a/src/assembler/asm.js b/src/assembler/asm.js index 189609d..0fe1b79 100644 --- a/src/assembler/asm.js +++ b/src/assembler/asm.js @@ -21,6 +21,8 @@ app.service('assembler', ['opcodes', function(opcodes) { var mapping = {}; // Hash map of label used to replace the labels after the assembler generated the code var labels = {}; + // Hash of uppercase labels used to detect duplicates + var normalizedLabels = {}; var lines = input.split('\n'); // Split text into code lines @@ -136,11 +138,7 @@ app.service('assembler', ['opcodes', function(opcodes) { }; // Allowed: Label var parseLabel = function(input) { - if (regexLabel.exec(input)) { - return input.toUpperCase(); - } else { - return undefined; - } + return regexLabel.exec(input) ? input : undefined; }; var getValue = function(input) { switch(input.slice(0,1)) { @@ -167,12 +165,12 @@ app.service('assembler', ['opcodes', function(opcodes) { } }; var addLabel = function(label) { - label = label.toUpperCase(); - if (label in labels) + var upperLabel = label.toUpperCase(); + if (upperLabel in normalizedLabels) throw "Duplicate label: " + label; - if (label === "A" || label === "B" || label === "C" || label === "D") - throw "Label contains keyword: " + label; + if (upperLabel === "A" || upperLabel === "B" || upperLabel === "C" || upperLabel === "D") + throw "Label contains keyword: " + upperLabel; labels[label] = code.length; }; diff --git a/src/emulator/cpu.js b/src/emulator/cpu.js index cca2aa9..7bc7c11 100644 --- a/src/emulator/cpu.js +++ b/src/emulator/cpu.js @@ -28,9 +28,9 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) { self.gpr[reg] = value; } else if(reg == self.gpr.length) { self.sp=value; - if (self.sp < 0) { // Not likely to happen, since we always get here after checkOpertion(). + if (self.sp < self.minSP) { // Not likely to happen, since we always get here after checkOpertion(). throw "Stack overflow"; - } else if (self.sp > 231) { + } else if (self.sp > self.maxSP) { throw "Stack underflow"; } } else { @@ -89,13 +89,13 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) { }; var push = function(value) { memory.store(self.sp--, value); - if (self.sp < 0) { + if (self.sp < self.minSP) { throw "Stack overflow"; } }; var pop = function() { var value = memory.load(++self.sp); - if (self.sp > 231) { + if (self.sp > self.maxSP) { throw "Stack underflow"; } @@ -567,9 +567,11 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) { }, reset: function() { var self = this; + self.maxSP = 231; + self.minSP = 0; self.gpr = [0, 0, 0, 0]; - self.sp = 231; + self.sp = self.maxSP; self.ip = 0; self.zero = false; self.carry = false; diff --git a/src/ui/controller.js b/src/ui/controller.js index 2a776fd..c0e35a6 100644 --- a/src/ui/controller.js +++ b/src/ui/controller.js @@ -1,12 +1,17 @@ -app.controller('Ctrl', ['$scope', '$timeout', 'cpu', 'memory', 'assembler', function($scope, $timeout, cpu, memory, assembler) { +app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'assembler', function($document, $scope, $timeout, cpu, memory, assembler) { $scope.memory = memory; $scope.cpu = cpu; $scope.error = ''; $scope.isRunning = false; $scope.displayHex = true; $scope.displayInstr = true; + $scope.displayA = false; + $scope.displayB = false; + $scope.displayC = false; + $scope.displayD = false; $scope.speeds = [{speed:1, desc:"1 HZ"}, {speed:4, desc:"4 HZ"}, {speed:8, desc:"8 HZ"}, {speed:16, desc:"16 HZ"}]; $scope.speed = 4; + $scope.outputStartIndex = 232; $scope.code = "; Simple example\n; Writes Hello World to the output\n\n JMP start\nhello: DB \"Hello World!\" ; Variable\n DB 0 ; String terminator\n\nstart:\n MOV C, hello ; Point to var \n MOV D, 232 ; Point to output\n CALL print\n HLT ; Stop execution\n\nprint: ; print(C:*from, D:*to)\n PUSH A\n PUSH B\n MOV B, 0\n.loop:\n MOV A, [C] ; Get char from var\n MOV [D], A ; Write to output\n INC C\n INC D \n CMP B, [C] ; Check if end\n JNZ .loop ; jump if not\n\n POP B\n POP A\n RET"; @@ -103,4 +108,46 @@ app.controller('Ctrl', ['$scope', '$timeout', 'cpu', 'memory', 'assembler', func } } }; + + $scope.jumpToLine = function(index) { + $document[0].getElementById('sourceCode').scrollIntoView(); + $scope.selectedLine = $scope.mapping[index]; + }; + + + $scope.isInstruction = function(index) { + return $scope.mapping !== undefined && + $scope.mapping[index] !== undefined && + $scope.displayInstr; + }; + + $scope.getMemoryCellCss = function(index) { + if (index >= $scope.outputStartIndex) { + return 'output-bg'; + } else if ($scope.isInstruction(index)) { + return 'instr-bg'; + } else if (index > cpu.sp && index <= cpu.maxSP) { + return 'stack-bg'; + } else { + return ''; + } + }; + + $scope.getMemoryInnerCellCss = function(index) { + if (index === cpu.ip) { + return 'marker marker-ip'; + } else if (index === cpu.sp) { + return 'marker marker-sp'; + } else if (index === cpu.gpr[0] && $scope.displayA) { + return 'marker marker-a'; + } else if (index === cpu.gpr[1] && $scope.displayB) { + return 'marker marker-b'; + } else if (index === cpu.gpr[2] && $scope.displayC) { + return 'marker marker-c'; + } else if (index === cpu.gpr[3] && $scope.displayD) { + return 'marker marker-d'; + } else { + return ''; + } + }; }]); \ No newline at end of file