From d6931753d9262eb149760715f280e8f6b71b6a91 Mon Sep 17 00:00:00 2001 From: Sergey Sorokin Date: Wed, 21 Jan 2015 19:08:52 +0300 Subject: [PATCH 1/2] Added possibility to use SP register in MOV, ADD, SUB, INC, DEC and CMP commands. Added optional highlighting of memory cells referenced by A, B, C and D registers. Added "Labels" panel which shows all labels in program, together with corresponding address and value from memory. --- assets/asmsimulator.js | 359 +++++++++++++++++++++++++------------ assets/asmsimulator.min.js | 4 +- assets/style.css | 23 +++ index.html | 77 ++++++-- instruction-set.html | 20 ++- src/assembler/asm.js | 53 ++++-- src/emulator/cpu.js | 120 ++++++++----- src/ui/controller.js | 1 + 8 files changed, 461 insertions(+), 196 deletions(-) diff --git a/assets/asmsimulator.js b/assets/asmsimulator.js index 543379a..61558ab 100644 --- a/assets/asmsimulator.js +++ b/assets/asmsimulator.js @@ -5,8 +5,12 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes // Use https://www.debuggex.com/ // Matches: "label: INSTRUCTION (["')OPERAND1(]"'), (["')OPERAND2(]"') - // GROUPS: 1 2 3 4 - var regex = /^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/; + // GROUPS: 1 2 3 7 + // var regex = /^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/; + // var regex = /^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[(\w+|SP(\+|-)\d+)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[(\w+|SP(\+|-)\d+)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/; + var regex = /^[\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*))?)?)?/; + var op1_group=3; // group indexes for operands + var op2_group=7; // MATCHES: "(+|-)INTEGER" var regexNum = /^[-+]?[0-9]+$/; // MATCHES: "(.L)abel" @@ -37,7 +41,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes throw "Invalid number format"; } }; - // Allowed registers: A, B, C, D + // Allowed registers: A, B, C, D, SP var parseRegister = function(input) { input = input.toUpperCase(); @@ -49,11 +53,57 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes return 2; } else if (input === 'D') { return 3; - } else { + } else if (input === 'SP') { + return 4; + } else { return undefined; } }; - // Allowed: Register, Label or Number + + var parseOffsetAddressing=function(input) { + input = input.toUpperCase(); + var m = 0; + var base = 0; + + if (input[0] === 'A') { + base = 0; + } else if (input[0] === 'B') { + base = 1; + } else if (input[0] === 'C') { + base = 2; + } else if (input[0] === 'D') { + base = 3; + } else if( input.slice(0,2) === "SP") { + base = 4; + } else { + return undefined; + } + var offset_start = 1; + if (base === 4) { + offset_start = 2; + } + + if (input[offset_start] === '-') { + m = -1; + } else if (input[offset_start] === '+') { + m = 1; + } else { + return undefined; + } + + var offset = m*parseInt(input.slice(offset_start+1),10); + + if (offset < -16 || offset > 15) + throw "offset must be a value between -16...+15"; + + if (offset < 0) { + offset=32+offset; // two's complement representation in 5-bit + } + + return offset*8+base; // shift offset 3 bits right and add 4 as code for SP register + }; + + // Allowed: Register, Label or Number; SP+/-Number is allowed for 'regaddress' type var parseRegOrNumber = function(input, typeReg, typeNumber) { var register = parseRegister(input); @@ -64,6 +114,15 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes if (label !== undefined) { return { type: typeNumber, value: label}; } else { + if (typeReg === "regaddress") { + + register = parseOffsetAddressing(input); + + if (register !== undefined) { + return { type: typeReg, value: register}; + } + } + var value = parseNumber(input); if (isNaN(value)) { @@ -118,6 +177,12 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes labels[label] = code.length; }; + + var checkNoExtraArg= function(instr, arg) { + if (arg !== undefined) { + throw instr+": too many arguments"; + } + }; for(var i = 0, l = lines.length; i < l; i++) { try { @@ -139,7 +204,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes switch(instr) { case 'DB': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); if (p1.type === "number") code.push(p1.value); @@ -151,9 +216,15 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes throw "DB does not support this operand"; break; + case 'HLT': + checkNoExtraArg('HLT',match[op1_group]); + opCode=opcodes.NONE; + code.push(opCode); + break; + case 'MOV': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.MOV_REG_TO_REG; @@ -177,8 +248,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'ADD': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.ADD_REG_TO_REG; @@ -194,8 +265,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'SUB': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.SUB_REG_FROM_REG; @@ -211,7 +282,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'INC': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg('INC',match[op2_group]); if (p1.type === "register") opCode = opcodes.INC_REG; @@ -222,7 +294,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case 'DEC': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg('DEC',match[op2_group]); if (p1.type === "register") opCode = opcodes.DEC_REG; @@ -233,8 +306,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case 'CMP': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.CMP_REG_WITH_REG; @@ -250,7 +323,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'JMP': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg('JMP',match[op2_group]); if (p1.type === "register") opCode = opcodes.JMP_REGADDRESS; @@ -262,7 +336,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'JC':case 'JB':case 'JNAE': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.JC_REGADDRESS; @@ -274,7 +349,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'JNC':case 'JNB':case 'JAE': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.JNC_REGADDRESS; @@ -286,7 +362,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'JZ': case 'JE': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.JZ_REGADDRESS; @@ -298,7 +375,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'JNZ': case 'JNE': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.JNZ_REGADDRESS; @@ -310,7 +388,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'JA': case 'JNBE': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.JA_REGADDRESS; @@ -322,7 +401,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'JNA': case 'JBE': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.JNA_REGADDRESS; @@ -334,7 +414,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'PUSH': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.PUSH_REG; @@ -350,7 +431,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'POP': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.POP_REG; @@ -360,7 +442,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'CALL': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.CALL_REGADDRESS; @@ -372,11 +455,16 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'RET': + checkNoExtraArg(instr,match[op1_group]); + opCode = opcodes.RET; + code.push(opCode); break; - case 'MUL': - p1 = getValue(match[3]); + + case 'MUL': + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.MUL_REG; @@ -392,7 +480,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'DIV': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.DIV_REG; @@ -408,8 +497,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'AND': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.AND_REG_WITH_REG; @@ -425,8 +514,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'OR': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.OR_REG_WITH_REG; @@ -442,8 +531,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'XOR': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.XOR_REG_WITH_REG; @@ -459,7 +548,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'NOT': - p1 = getValue(match[3]); + p1 = getValue(match[op1_group]); + checkNoExtraArg(instr,match[op2_group]); if (p1.type === "register") opCode = opcodes.NOT_REG; @@ -469,8 +559,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value); break; case 'SHL':case 'SAL': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.SHL_REG_WITH_REG; @@ -486,8 +576,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes code.push(opCode, p1.value, p2.value); break; case 'SHR': case 'SAR': - p1 = getValue(match[3]); - p2 = getValue(match[4]); + p1 = getValue(match[op1_group]); + p2 = getValue(match[op2_group]); if (p1.type === "register" && p2.type === "register") opCode = opcodes.SHR_REG_WITH_REG; @@ -530,11 +620,10 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes } } - return { code: code, mapping: mapping }; + return { code: code, mapping: mapping, labels: labels }; } }; -}]); -;app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) { +}]);;app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) { var cpu = { step: function() { var self = this; @@ -551,6 +640,55 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes return reg; } }; + var checkGPR_SP = function(reg) { + if (reg < 0 || reg >= 1+self.gpr.length) { + throw "Invalid register: " + reg; + } else { + return reg; + } + }; + var setGPR_SP = function(reg,value) + { + if(reg >= 0 && reg 231) { + throw "Stack underflow"; + } + } else { + throw "Invalid register: " + reg; + } + }; + var getGPR_SP = function(reg) + { + if(reg >= 0 && reg 15 ) { + offset = offset - 32; + } + + return base+offset; + }; var checkOperation = function(value) { self.zero = false; self.carry = false; @@ -606,39 +744,39 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes case opcodes.NONE: return false; // Abort step case opcodes.MOV_REG_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = self.gpr[regFrom]; + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = checkGPR_SP(memory.load(++self.ip)); + setGPR_SP(regTo,getGPR_SP(regFrom)); self.ip++; break; case opcodes.MOV_ADDRESS_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); memFrom = memory.load(++self.ip); - self.gpr[regTo] = memory.load(memFrom); + setGPR_SP(regTo,memory.load(memFrom)); self.ip++; break; case opcodes.MOV_REGADDRESS_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = memory.load(self.gpr[regFrom]); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = memory.load(++self.ip); + setGPR_SP(regTo,memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.MOV_REG_TO_ADDRESS: memTo = memory.load(++self.ip); - regFrom = checkGPR(memory.load(++self.ip)); - memory.store(memTo, self.gpr[regFrom]); + regFrom = checkGPR_SP(memory.load(++self.ip)); + memory.store(memTo, getGPR_SP(regFrom)); self.ip++; break; case opcodes.MOV_REG_TO_REGADDRESS: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - memory.store(self.gpr[regTo], self.gpr[regFrom]); + regTo = memory.load(++self.ip); + regFrom = checkGPR_SP(memory.load(++self.ip)); + memory.store(indirectRegisterAddress(regTo), getGPR_SP(regFrom)); self.ip++; break; case opcodes.MOV_NUMBER_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); number = memory.load(++self.ip); - self.gpr[regTo] = number; + setGPR_SP(regTo,number); self.ip++; break; case opcodes.MOV_NUMBER_TO_ADDRESS: @@ -648,91 +786,91 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes self.ip++; break; case opcodes.MOV_NUMBER_TO_REGADDRESS: - regTo = checkGPR(memory.load(++self.ip)); + regTo = memory.load(++self.ip); number = memory.load(++self.ip); - memory.store(self.gpr[regTo], number); + memory.store(indirectRegisterAddress(regTo), number); self.ip++; break; case opcodes.ADD_REG_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] + self.gpr[regFrom]); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = checkGPR_SP(memory.load(++self.ip)); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) + getGPR_SP(regFrom))); self.ip++; break; case opcodes.ADD_REGADDRESS_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] + memory.load(self.gpr[regFrom])); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = memory.load(++self.ip); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) + memory.load(indirectRegisterAddress(regFrom)))); self.ip++; break; case opcodes.ADD_ADDRESS_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); memFrom = memory.load(++self.ip); - self.gpr[regTo] = checkOperation(self.gpr[regTo] + memory.load(memFrom)); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) + memory.load(memFrom))); self.ip++; break; case opcodes.ADD_NUMBER_TO_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); number = memory.load(++self.ip); - self.gpr[regTo] = checkOperation(self.gpr[regTo] + number); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) + number)); self.ip++; break; case opcodes.SUB_REG_FROM_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] - self.gpr[regFrom]); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = checkGPR_SP(memory.load(++self.ip)); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) - self.gpr[regFrom])); self.ip++; break; case opcodes.SUB_REGADDRESS_FROM_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] - memory.load(self.gpr[regFrom])); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = memory.load(++self.ip); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) - memory.load(indirectRegisterAddress(regFrom)))); self.ip++; break; case opcodes.SUB_ADDRESS_FROM_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); memFrom = memory.load(++self.ip); - self.gpr[regTo] = checkOperation(self.gpr[regTo] - memory.load(memFrom)); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) - memory.load(memFrom))); self.ip++; break; case opcodes.SUB_NUMBER_FROM_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); number = memory.load(++self.ip); - self.gpr[regTo] = checkOperation(self.gpr[regTo] - number); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) - number)); self.ip++; break; case opcodes.INC_REG: - regTo = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] + 1); + regTo = checkGPR_SP(memory.load(++self.ip)); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) + 1)); self.ip++; break; case opcodes.DEC_REG: - regTo = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] - 1); + regTo = checkGPR_SP(memory.load(++self.ip)); + setGPR_SP(regTo,checkOperation(getGPR_SP(regTo) - 1)); self.ip++; break; case opcodes.CMP_REG_WITH_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - checkOperation(self.gpr[regTo] - self.gpr[regFrom]); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = checkGPR_SP(memory.load(++self.ip)); + checkOperation(getGPR_SP(regTo) - getGPR_SP(regFrom)); self.ip++; break; case opcodes.CMP_REGADDRESS_WITH_REG: - regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - checkOperation(self.gpr[regTo] - memory.load(self.gpr[regFrom])); + regTo = checkGPR_SP(memory.load(++self.ip)); + regFrom = memory.load(++self.ip); + checkOperation(getGPR_SP(regTo) - memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.CMP_ADDRESS_WITH_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); memFrom = memory.load(++self.ip); - checkOperation(self.gpr[regTo] - memory.load(memFrom)); + checkOperation(getGPR_SP(regTo) - memory.load(memFrom)); self.ip++; break; case opcodes.CMP_NUMBER_WITH_REG: - regTo = checkGPR(memory.load(++self.ip)); + regTo = checkGPR_SP(memory.load(++self.ip)); number = memory.load(++self.ip); - checkOperation(self.gpr[regTo] - number); + checkOperation(getGPR_SP(regTo) - number); self.ip++; break; case opcodes.JMP_REGADDRESS: @@ -845,8 +983,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes self.ip++; break; case opcodes.PUSH_REGADDRESS: - regFrom = checkGPR(memory.load(++self.ip)); - push(memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + push(memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.PUSH_ADDRESS: @@ -883,8 +1021,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes self.ip++; break; case opcodes.MUL_REGADDRESS: // A = A * [REG] - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[0] = checkOperation(self.gpr[0] * memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + self.gpr[0] = checkOperation(self.gpr[0] * memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.MUL_ADDRESS: // A = A * [NUMBER] @@ -903,8 +1041,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes self.ip++; break; case opcodes.DIV_REGADDRESS: // A = A / [REG] - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[0] = checkOperation(division(memory.load(self.gpr[regFrom]))); + regFrom = memory.load(++self.ip); + self.gpr[0] = checkOperation(division(memory.load(indirectRegisterAddress(regFrom)))); self.ip++; break; case opcodes.DIV_ADDRESS: // A = A / [NUMBER] @@ -925,8 +1063,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case opcodes.AND_REGADDRESS_WITH_REG: regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] & memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + self.gpr[regTo] = checkOperation(self.gpr[regTo] & memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.AND_ADDRESS_WITH_REG: @@ -949,8 +1087,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case opcodes.OR_REGADDRESS_WITH_REG: regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] | memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + self.gpr[regTo] = checkOperation(self.gpr[regTo] | memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.OR_ADDRESS_WITH_REG: @@ -973,8 +1111,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case opcodes.XOR_REGADDRESS_WITH_REG: regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] ^ memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + self.gpr[regTo] = checkOperation(self.gpr[regTo] ^ memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.XOR_ADDRESS_WITH_REG: @@ -1002,8 +1140,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case opcodes.SHL_REGADDRESS_WITH_REG: regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] << memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + self.gpr[regTo] = checkOperation(self.gpr[regTo] << memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.SHL_ADDRESS_WITH_REG: @@ -1026,8 +1164,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes break; case opcodes.SHR_REGADDRESS_WITH_REG: regTo = checkGPR(memory.load(++self.ip)); - regFrom = checkGPR(memory.load(++self.ip)); - self.gpr[regTo] = checkOperation(self.gpr[regTo] >>> memory.load(self.gpr[regFrom])); + regFrom = memory.load(++self.ip); + self.gpr[regTo] = checkOperation(self.gpr[regTo] >>> memory.load(indirectRegisterAddress(regFrom))); self.ip++; break; case opcodes.SHR_ADDRESS_WITH_REG: @@ -1269,6 +1407,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes var assembly = assembler.go($scope.code); $scope.mapping = assembly.mapping; var binary = assembly.code; + $scope.labels = assembly.labels; if (binary.length > memory.data.length) throw "Binary code does not fit into the memory. Max " + memory.data.length + " bytes are allowed"; diff --git a/assets/asmsimulator.min.js b/assets/asmsimulator.min.js index c29f154..67915ed 100644 --- a/assets/asmsimulator.min.js +++ b/assets/asmsimulator.min.js @@ -1,2 +1,2 @@ -/*! asmsimulator 16-09-2014 */ -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+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/,d=/^[-+]?[0-9]+$/,e=/^[.A-Za-z]\w*$/,f=[],g={},h={},i=b.split("\n"),j=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(d.exec(a))return parseInt(a,10);throw"Invalid number format"},k=function(a){return a=a.toUpperCase(),"A"===a?0:"B"===a?1:"C"===a?2:"D"===a?3:void 0},l=function(a,b,c){var d=k(a);if(void 0!==d)return{type:b,value:d};var e=m(a);if(void 0!==e)return{type:c,value:e};var f=j(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}},m=function(a){return e.exec(a)?a.toUpperCase():void 0},n=function(a){switch(a.slice(0,1)){case"[":var b=a.slice(1,a.length-1);return l(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 l(a,"register","number")}},o=(function(a){if(a=a.toUpperCase(),a in h)throw"Duplicate label: "+a;if("A"===a||"B"===a||"C"===a||"D"===a)throw"Label contains keyword: "+a;h[a]=f.length}),p=0,q=i.length;q>p;p++)try{var r=c.exec(i[p]);if(void 0!==r[1]||void 0!==r[2]){if(void 0!==r[1]&&o(r[1]),void 0!==r[2]){var s,t,u,v=r[2].toUpperCase();switch("DB"!==v&&(g[f.length]=p),v){case"DB":if(s=n(r[3]),"number"===s.type)f.push(s.value);else{if("numbers"!==s.type)throw"DB does not support this operand";for(var w=0,x=s.value.length;x>w;w++)f.push(s.value[w])}break;case"MOV":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.MOV_REG_TO_REG;else if("register"===s.type&&"address"===t.type)u=a.MOV_ADDRESS_TO_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.MOV_REGADDRESS_TO_REG;else if("address"===s.type&&"register"===t.type)u=a.MOV_REG_TO_ADDRESS;else if("regaddress"===s.type&&"register"===t.type)u=a.MOV_REG_TO_REGADDRESS;else if("register"===s.type&&"number"===t.type)u=a.MOV_NUMBER_TO_REG;else if("address"===s.type&&"number"===t.type)u=a.MOV_NUMBER_TO_ADDRESS;else{if("regaddress"!==s.type||"number"!==t.type)throw"MOV does not support this operands";u=a.MOV_NUMBER_TO_REGADDRESS}f.push(u,s.value,t.value);break;case"ADD":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.ADD_REG_TO_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.ADD_REGADDRESS_TO_REG;else if("register"===s.type&&"address"===t.type)u=a.ADD_ADDRESS_TO_REG;else{if("register"!==s.type||"number"!==t.type)throw"ADD does not support this operands";u=a.ADD_NUMBER_TO_REG}f.push(u,s.value,t.value);break;case"SUB":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.SUB_REG_FROM_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.SUB_REGADDRESS_FROM_REG;else if("register"===s.type&&"address"===t.type)u=a.SUB_ADDRESS_FROM_REG;else{if("register"!==s.type||"number"!==t.type)throw"SUB does not support this operands";u=a.SUB_NUMBER_FROM_REG}f.push(u,s.value,t.value);break;case"INC":if(s=n(r[3]),"register"!==s.type)throw"INC does not support this operand";u=a.INC_REG,f.push(u,s.value);break;case"DEC":if(s=n(r[3]),"register"!==s.type)throw"DEC does not support this operand";u=a.DEC_REG,f.push(u,s.value);break;case"CMP":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.CMP_REG_WITH_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.CMP_REGADDRESS_WITH_REG;else if("register"===s.type&&"address"===t.type)u=a.CMP_ADDRESS_WITH_REG;else{if("register"!==s.type||"number"!==t.type)throw"CMP does not support this operands";u=a.CMP_NUMBER_WITH_REG}f.push(u,s.value,t.value);break;case"JMP":if(s=n(r[3]),"register"===s.type)u=a.JMP_REGADDRESS;else{if("number"!==s.type)throw"JMP does not support this operands";u=a.JMP_ADDRESS}f.push(u,s.value);break;case"JC":case"JB":case"JNAE":if(s=n(r[3]),"register"===s.type)u=a.JC_REGADDRESS;else{if("number"!==s.type)throw v+" does not support this operand";u=a.JC_ADDRESS}f.push(u,s.value);break;case"JNC":case"JNB":case"JAE":if(s=n(r[3]),"register"===s.type)u=a.JNC_REGADDRESS;else{if("number"!==s.type)throw v+"does not support this operand";u=a.JNC_ADDRESS}f.push(u,s.value);break;case"JZ":case"JE":if(s=n(r[3]),"register"===s.type)u=a.JZ_REGADDRESS;else{if("number"!==s.type)throw v+" does not support this operand";u=a.JZ_ADDRESS}f.push(u,s.value);break;case"JNZ":case"JNE":if(s=n(r[3]),"register"===s.type)u=a.JNZ_REGADDRESS;else{if("number"!==s.type)throw v+" does not support this operand";u=a.JNZ_ADDRESS}f.push(u,s.value);break;case"JA":case"JNBE":if(s=n(r[3]),"register"===s.type)u=a.JA_REGADDRESS;else{if("number"!==s.type)throw v+" does not support this operand";u=a.JA_ADDRESS}f.push(u,s.value);break;case"JNA":case"JBE":if(s=n(r[3]),"register"===s.type)u=a.JNA_REGADDRESS;else{if("number"!==s.type)throw v+" does not support this operand";u=a.JNA_ADDRESS}f.push(u,s.value);break;case"PUSH":if(s=n(r[3]),"register"===s.type)u=a.PUSH_REG;else if("regaddress"===s.type)u=a.PUSH_REGADDRESS;else if("address"===s.type)u=a.PUSH_ADDRESS;else{if("number"!==s.type)throw"PUSH does not support this operand";u=a.PUSH_NUMBER}f.push(u,s.value);break;case"POP":if(s=n(r[3]),"register"!==s.type)throw"PUSH does not support this operand";u=a.POP_REG,f.push(u,s.value);break;case"CALL":if(s=n(r[3]),"register"===s.type)u=a.CALL_REGADDRESS;else{if("number"!==s.type)throw"CALL does not support this operand";u=a.CALL_ADDRESS}f.push(u,s.value);break;case"RET":u=a.RET,f.push(u);break;case"MUL":if(s=n(r[3]),"register"===s.type)u=a.MUL_REG;else if("regaddress"===s.type)u=a.MUL_REGADDRESS;else if("address"===s.type)u=a.MUL_ADDRESS;else{if("number"!==s.type)throw"MULL does not support this operand";u=a.MUL_NUMBER}f.push(u,s.value);break;case"DIV":if(s=n(r[3]),"register"===s.type)u=a.DIV_REG;else if("regaddress"===s.type)u=a.DIV_REGADDRESS;else if("address"===s.type)u=a.DIV_ADDRESS;else{if("number"!==s.type)throw"DIV does not support this operand";u=a.DIV_NUMBER}f.push(u,s.value);break;case"AND":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.AND_REG_WITH_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.AND_REGADDRESS_WITH_REG;else if("register"===s.type&&"address"===t.type)u=a.AND_ADDRESS_WITH_REG;else{if("register"!==s.type||"number"!==t.type)throw"AND does not support this operands";u=a.AND_NUMBER_WITH_REG}f.push(u,s.value,t.value);break;case"OR":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.OR_REG_WITH_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.OR_REGADDRESS_WITH_REG;else if("register"===s.type&&"address"===t.type)u=a.OR_ADDRESS_WITH_REG;else{if("register"!==s.type||"number"!==t.type)throw"OR does not support this operands";u=a.OR_NUMBER_WITH_REG}f.push(u,s.value,t.value);break;case"XOR":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.XOR_REG_WITH_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.XOR_REGADDRESS_WITH_REG;else if("register"===s.type&&"address"===t.type)u=a.XOR_ADDRESS_WITH_REG;else{if("register"!==s.type||"number"!==t.type)throw"XOR does not support this operands";u=a.XOR_NUMBER_WITH_REG}f.push(u,s.value,t.value);break;case"NOT":if(s=n(r[3]),"register"!==s.type)throw"NOT does not support this operand";u=a.NOT_REG,f.push(u,s.value);break;case"SHL":case"SAL":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.SHL_REG_WITH_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.SHL_REGADDRESS_WITH_REG;else if("register"===s.type&&"address"===t.type)u=a.SHL_ADDRESS_WITH_REG;else{if("register"!==s.type||"number"!==t.type)throw v+" does not support this operands";u=a.SHL_NUMBER_WITH_REG}f.push(u,s.value,t.value);break;case"SHR":case"SAR":if(s=n(r[3]),t=n(r[4]),"register"===s.type&&"register"===t.type)u=a.SHR_REG_WITH_REG;else if("register"===s.type&&"regaddress"===t.type)u=a.SHR_REGADDRESS_WITH_REG;else if("register"===s.type&&"address"===t.type)u=a.SHR_ADDRESS_WITH_REG;else{if("register"!==s.type||"number"!==t.type)throw v+" does not support this operands";u=a.SHR_NUMBER_WITH_REG}f.push(u,s.value,t.value);break;default:throw"Invalid instruction: "+r[2]}}}else{var y=i[p].trim();if(""!==y&&";"!==y.slice(0,1))throw"Syntax error"}}catch(z){throw{error:z,line:p}}for(p=0,q=f.length;q>p;p++)if(!angular.isNumber(f[p])){if(!(f[p]in h))throw{error:"Undefined label: "+f[p]};f[p]=h[f[p]]}return{code:f,mapping:g}}}}]),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){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},f=function(a){if(0>a||a>=b.data.length)throw"IP outside memory";c.ip=a},g=function(a){if(b.store(c.sp--,a),c.sp<0)throw"Stack overflow"},h=function(){var a=b.load(++c.sp);if(c.sp>231)throw"Stack underflow";return a},i=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 j,k,l,m,n,o=b.load(c.ip);switch(o){case a.NONE:return!1;case a.MOV_REG_TO_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=c.gpr[k],c.ip++;break;case a.MOV_ADDRESS_TO_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=b.load(l),c.ip++;break;case a.MOV_REGADDRESS_TO_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=b.load(c.gpr[k]),c.ip++;break;case a.MOV_REG_TO_ADDRESS:m=b.load(++c.ip),k=d(b.load(++c.ip)),b.store(m,c.gpr[k]),c.ip++;break;case a.MOV_REG_TO_REGADDRESS:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),b.store(c.gpr[j],c.gpr[k]),c.ip++;break;case a.MOV_NUMBER_TO_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=n,c.ip++;break;case a.MOV_NUMBER_TO_ADDRESS:m=b.load(++c.ip),n=b.load(++c.ip),b.store(m,n),c.ip++;break;case a.MOV_NUMBER_TO_REGADDRESS:j=d(b.load(++c.ip)),n=b.load(++c.ip),b.store(c.gpr[j],n),c.ip++;break;case a.ADD_REG_TO_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]+c.gpr[k]),c.ip++;break;case a.ADD_REGADDRESS_TO_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]+b.load(c.gpr[k])),c.ip++;break;case a.ADD_ADDRESS_TO_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]+b.load(l)),c.ip++;break;case a.ADD_NUMBER_TO_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]+n),c.ip++;break;case a.SUB_REG_FROM_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]-c.gpr[k]),c.ip++;break;case a.SUB_REGADDRESS_FROM_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]-b.load(c.gpr[k])),c.ip++;break;case a.SUB_ADDRESS_FROM_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]-b.load(l)),c.ip++;break;case a.SUB_NUMBER_FROM_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]-n),c.ip++;break;case a.INC_REG:j=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]+1),c.ip++;break;case a.DEC_REG:j=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]-1),c.ip++;break;case a.CMP_REG_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),e(c.gpr[j]-c.gpr[k]),c.ip++;break;case a.CMP_REGADDRESS_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),e(c.gpr[j]-b.load(c.gpr[k])),c.ip++;break;case a.CMP_ADDRESS_WITH_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),e(c.gpr[j]-b.load(l)),c.ip++;break;case a.CMP_NUMBER_WITH_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),e(c.gpr[j]-n),c.ip++;break;case a.JMP_REGADDRESS:j=d(b.load(++c.ip)),f(c.gpr[j]);break;case a.JMP_ADDRESS:n=b.load(++c.ip),f(n);break;case a.JC_REGADDRESS:j=d(b.load(++c.ip)),c.carry?f(c.gpr[j]):c.ip++;break;case a.JC_ADDRESS:n=b.load(++c.ip),c.carry?f(n):c.ip++;break;case a.JNC_REGADDRESS:j=d(b.load(++c.ip)),c.carry?c.ip++:f(c.gpr[j]);break;case a.JNC_ADDRESS:n=b.load(++c.ip),c.carry?c.ip++:f(n);break;case a.JZ_REGADDRESS:j=d(b.load(++c.ip)),c.zero?f(c.gpr[j]):c.ip++;break;case a.JZ_ADDRESS:n=b.load(++c.ip),c.zero?f(n):c.ip++;break;case a.JNZ_REGADDRESS:j=d(b.load(++c.ip)),c.zero?c.ip++:f(c.gpr[j]);break;case a.JNZ_ADDRESS:n=b.load(++c.ip),c.zero?c.ip++:f(n);break;case a.JA_REGADDRESS:j=d(b.load(++c.ip)),c.zero||c.carry?c.ip++:f(c.gpr[j]);break;case a.JA_ADDRESS:n=b.load(++c.ip),c.zero||c.carry?c.ip++:f(n);break;case a.JNA_REGADDRESS:j=d(b.load(++c.ip)),c.zero||c.carry?f(c.gpr[j]):c.ip++;break;case a.JNA_ADDRESS:n=b.load(++c.ip),c.zero||c.carry?f(n):c.ip++;break;case a.PUSH_REG:k=d(b.load(++c.ip)),g(c.gpr[k]),c.ip++;break;case a.PUSH_REGADDRESS:k=d(b.load(++c.ip)),g(b.load(c.gpr[k])),c.ip++;break;case a.PUSH_ADDRESS:l=b.load(++c.ip),g(b.load(l)),c.ip++;break;case a.PUSH_NUMBER:n=b.load(++c.ip),g(n),c.ip++;break;case a.POP_REG:j=d(b.load(++c.ip)),c.gpr[j]=h(),c.ip++;break;case a.CALL_REGADDRESS:j=d(b.load(++c.ip)),g(c.ip+1),f(c.gpr[j]);break;case a.CALL_ADDRESS:n=b.load(++c.ip),g(c.ip+1),f(n);break;case a.RET:f(h());break;case a.MUL_REG:k=d(b.load(++c.ip)),c.gpr[0]=e(c.gpr[0]*c.gpr[k]),c.ip++;break;case a.MUL_REGADDRESS:k=d(b.load(++c.ip)),c.gpr[0]=e(c.gpr[0]*b.load(c.gpr[k])),c.ip++;break;case a.MUL_ADDRESS:l=b.load(++c.ip),c.gpr[0]=e(c.gpr[0]*b.load(l)),c.ip++;break;case a.MUL_NUMBER:n=b.load(++c.ip),c.gpr[0]=e(c.gpr[0]*n),c.ip++;break;case a.DIV_REG:k=d(b.load(++c.ip)),c.gpr[0]=e(i(c.gpr[k])),c.ip++;break;case a.DIV_REGADDRESS:k=d(b.load(++c.ip)),c.gpr[0]=e(i(b.load(c.gpr[k]))),c.ip++;break;case a.DIV_ADDRESS:l=b.load(++c.ip),c.gpr[0]=e(i(b.load(l))),c.ip++;break;case a.DIV_NUMBER:n=b.load(++c.ip),c.gpr[0]=e(i(n)),c.ip++;break;case a.AND_REG_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]&c.gpr[k]),c.ip++;break;case a.AND_REGADDRESS_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]&b.load(c.gpr[k])),c.ip++;break;case a.AND_ADDRESS_WITH_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]&b.load(l)),c.ip++;break;case a.AND_NUMBER_WITH_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]&n),c.ip++;break;case a.OR_REG_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]|c.gpr[k]),c.ip++;break;case a.OR_REGADDRESS_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]|b.load(c.gpr[k])),c.ip++;break;case a.OR_ADDRESS_WITH_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]|b.load(l)),c.ip++;break;case a.OR_NUMBER_WITH_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]|n),c.ip++;break;case a.XOR_REG_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]^c.gpr[k]),c.ip++;break;case a.XOR_REGADDRESS_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]^b.load(c.gpr[k])),c.ip++;break;case a.XOR_ADDRESS_WITH_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]^b.load(l)),c.ip++;break;case a.XOR_NUMBER_WITH_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]^n),c.ip++;break;case a.NOT_REG:j=d(b.load(++c.ip)),c.gpr[j]=e(~c.gpr[j]),c.ip++;break;case a.SHL_REG_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]<>>c.gpr[k]),c.ip++;break;case a.SHR_REGADDRESS_WITH_REG:j=d(b.load(++c.ip)),k=d(b.load(++c.ip)),c.gpr[j]=e(c.gpr[j]>>>b.load(c.gpr[k])),c.ip++;break;case a.SHR_ADDRESS_WITH_REG:j=d(b.load(++c.ip)),l=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]>>>b.load(l)),c.ip++;break;case a.SHR_NUMBER_WITH_REG:j=d(b.load(++c.ip)),n=b.load(++c.ip),c.gpr[j]=e(c.gpr[j]>>>n),c.ip++;break;default:throw"Invalid op code: "+o}return!0}catch(p){throw c.fault=!0,p}},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 DB 0 ; 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(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},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 DB 0 ; 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;eCode ( -
-

Output

-
-
-
- {{ getChar(m) }} -
-
- +
+
+
+
+

Output

+
+
+
+ {{ getChar(m) }} +
+
+
+
+
+
+
+

Labels

+
+
+ + + + + + + + + + + +
NameAddressValue
{{ name }}{{ value | number:displayHex }}{{ memory.data[value] | number:displayHex }} + + ('{{ getChar(memory.data[value]) }}') + +
+
+
+
+
@@ -82,10 +111,10 @@

CPU & Memory

- {{ cpu.gpr[0] | number:displayHex }} - {{ cpu.gpr[1] | number:displayHex }} - {{ cpu.gpr[2] | number:displayHex }} - {{ cpu.gpr[3] | number:displayHex }} +
{{ cpu.gpr[0] | number:displayHex }}
+
{{ cpu.gpr[1] | number:displayHex }}
+
{{ cpu.gpr[2] | number:displayHex }}
+
{{ cpu.gpr[3] | number:displayHex }}
{{ cpu.ip | number:displayHex }}
{{ cpu.sp | number:displayHex }}
{{ cpu.zero | flag }} @@ -99,7 +128,7 @@

CPU & Memory

diff --git a/instruction-set.html b/instruction-set.html index f8c7dd3..0d5f7de 100644 --- a/instruction-set.html +++ b/instruction-set.html @@ -43,18 +43,22 @@

Syntax

Character: 'A' String: "Hello World!" -

Operands can either be one of the four general purpose registers, a memory address or a constant. Instead of defining an address as a constant or by using a register you can use labels. The assembler will then replace the label with the corresponding constant.

+

Operands can either be one of the four general purpose registers, stack pointer register, a memory address or a constant. + Stack pointer register can only be used as operand in MOV, ADD, SUB, CMP, INC and DEC instructions. + Instead of defining an address as a constant or by using a register you can use labels. The assembler will then replace the label with the corresponding constant.

-Register: A, B, C, D
-Address using a register: [A]
+General purpose (GP) register: A, B, C, D
+Stack pointer register: SP
+Address using a GP register: [A]
+Address using a GP register and offset: [D-3]
 Address using SP register and offset: [SP+2]
 Address using a constant: [100]
 Address using a label: label
 Constant: Any number between 0..255 (8bit unsigned)
-Offset for indirect addressing with SP: Integer between -16..+15 (sign is mandatory)
+Offset for indirect addressing: Integer between -16..+15 (sign is mandatory)
 

MOV - Copy a value

-

Copies a value from src to dest. The MOV instruction is the only one able to directly modify the memory.

+

Copies a value from src to dest. The MOV instruction is the only one able to directly modify the memory. SP can be used as operand with MOV.

 MOV reg, reg
 MOV reg, address
@@ -69,7 +73,7 @@ 

DB - Variable

Math operations

Addition and Subtraction -

Adds two numbers together or subtract one number form another. This operations will modify the carry and zero flag.

+

Adds two numbers together or subtract one number form another. This operations will modify the carry and zero flag. SP can be used as operand with ADD and SUB.

 ADD reg, reg
 ADD reg, address
@@ -79,7 +83,7 @@ 

Math operations

SUB reg, constant
Increment and Decrement -

Increments or decrements a register by one. This operations will modify the carry and zero flag.

+

Increments or decrements a register by one. This operations will modify the carry and zero flag. SP can be used as operand with INC and DEC.

 INC reg
 DEC reg
@@ -119,7 +123,7 @@ 

Math operations

SHR reg, constant

CMP - Compare

-

Compares two values and sets the zero flag to true if they are equal. Use this instruction before a conditional jump.

+

Compares two values and sets the zero flag to true if they are equal. SP can be used as operand with CMP. Use this instruction before a conditional jump.

 CMP reg, reg
 CMP reg, address
diff --git a/src/assembler/asm.js b/src/assembler/asm.js
index 3db534a..acd73c8 100644
--- a/src/assembler/asm.js
+++ b/src/assembler/asm.js
@@ -5,11 +5,12 @@ app.service('assembler', ['opcodes', function(opcodes) {
 
             // Use https://www.debuggex.com/
             // Matches: "label: INSTRUCTION (["')OPERAND1(]"'), (["')OPERAND2(]"')
-            // GROUPS:      1       2            3                 6
+            // GROUPS:      1       2            3                 7
             // var regex = /^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[\w+\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/;
-			var regex = /^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[(\w+|SP(\+|-)\d+)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[(\w+|SP(\+|-)\d+)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/;
+			// var regex = /^[\t ]*(?:([.A-Za-z]\w*)[:])?(?:[\t ]*([A-Za-z]{2,4})(?:[\t ]+(\[(\w+|SP(\+|-)\d+)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*)(?:[\t ]*[,][\t ]*(\[(\w+|SP(\+|-)\d+)\]|\".+?\"|\'.+?\'|[.A-Za-z0-9]\w*))?)?)?/;
+			var regex = /^[\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*))?)?)?/;
 			var op1_group=3;	// group indexes for operands
-			var op2_group=6;
+			var op2_group=7;
             // MATCHES: "(+|-)INTEGER"
             var regexNum = /^[-+]?[0-9]+$/;
             // MATCHES: "(.L)abel"
@@ -40,7 +41,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
                     throw "Invalid number format";
                 }
             };
-            // Allowed registers: A, B, C, D
+            // Allowed registers: A, B, C, D, SP
             var parseRegister = function(input) {
                 input = input.toUpperCase();
 
@@ -52,23 +53,45 @@ app.service('assembler', ['opcodes', function(opcodes) {
                     return 2;
                 } else if (input === 'D') {
                     return 3;
-                } else {
+                } else if (input === 'SP') {
+					return 4;
+				} else {
                     return undefined;
                 }
             };
 			
-			var parseSPAddressing=function(input) {
+			var parseOffsetAddressing=function(input) {
 				input = input.toUpperCase();
-				var m=0;
+				var m = 0;
+				var base = 0;
+				
+				if (input[0] === 'A') {
+					base = 0;
+				} else if (input[0] === 'B') {
+					base = 1;
+				} else if (input[0] === 'C') {
+					base = 2;
+				} else if (input[0] === 'D') {
+					base = 3;
+				} else if( input.slice(0,2) === "SP") {
+					base = 4;
+				} else {
+					return undefined;
+				}
+				var offset_start = 1;
+				if (base === 4) {
+					offset_start = 2;
+				}
 				
-				if(input.slice(0,3) === "SP+") {
-					m=1;
-				} else if(input.slice(0,3) === "SP-") {
-					m=-1;
+				if (input[offset_start] === '-') {
+					m = -1;
+				} else if (input[offset_start] === '+') {
+					m = 1;
 				} else {
 					return undefined;
 				}
-				var offset = m*parseInt(input.slice(3),10);
+				
+				var offset = m*parseInt(input.slice(offset_start+1),10);
 				
 				if (offset < -16 || offset > 15)
 					throw "offset must be a value between -16...+15";
@@ -77,7 +100,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
 					offset=32+offset;	// two's complement representation in 5-bit
 				}
 				
-				return offset*8+4;		// shift offset 3 bits right and add 4 as code for SP register
+				return offset*8+base;		// shift offset 3 bits right and add 4 as code for SP register
 			};
 			
             // Allowed: Register, Label or Number; SP+/-Number is allowed for 'regaddress' type
@@ -93,7 +116,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
                     } else {
 						if (typeReg === "regaddress") {
 						
-							register = parseSPAddressing(input);
+							register = parseOffsetAddressing(input);
 						
 							if (register !== undefined) {
 								return { type: typeReg, value: register};
@@ -597,7 +620,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
                 }
             }
 
-            return { code: code, mapping: mapping };
+            return { code: code, mapping: mapping, labels: labels };
         }
     };
 }]);
\ No newline at end of file
diff --git a/src/emulator/cpu.js b/src/emulator/cpu.js
index f88d2ea..cca2aa9 100644
--- a/src/emulator/cpu.js
+++ b/src/emulator/cpu.js
@@ -15,6 +15,38 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
                         return reg;
                     }
                 };
+                var checkGPR_SP = function(reg) {
+                    if (reg < 0 || reg >= 1+self.gpr.length) {
+						throw "Invalid register: " + reg;
+                    } else {
+                        return reg;
+                    }
+                };
+				var setGPR_SP = function(reg,value)
+				{
+					if(reg >= 0 && reg  231) {
+							throw "Stack underflow";
+						}
+					} else {
+						throw "Invalid register: " + reg;
+					}
+				};
+				var getGPR_SP = function(reg)
+				{
+					if(reg >= 0 && reg  memory.data.length)
                 throw "Binary code does not fit into the memory. Max " + memory.data.length + " bytes are allowed";

From 31fc8ec977aea027bc5d25a074ab1a3779dfd09b Mon Sep 17 00:00:00 2001
From: Schweigi 
Date: Fri, 23 Jan 2015 22:30:52 +0100
Subject: [PATCH 2/2] Use newest Angular and Bootstrap version and pump version
 to 0.4.0

---
 README.md            | 2 +-
 index.html           | 6 +++---
 instruction-set.html | 4 ++--
 package.json         | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 6f11134..c953ef7 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Run `grunt` to build the project.
 ### License
 **The MIT License**
 
-Copyright (c) 2014 Marco Schweighauser
+Copyright (c) 2015 Marco Schweighauser
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 
diff --git a/index.html b/index.html
index a074ffc..069c1f8 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,7 @@
 
     Simple 8-bit Assembler Simulator in Javascript
     
-    
+    
     
     
     
@@ -164,9 +164,9 @@ 

CPU & Memory


-

by Marco Schweighauser (2014) | MIT License

+

by Marco Schweighauser (2015) | MIT License

- + \ No newline at end of file diff --git a/instruction-set.html b/instruction-set.html index 61a358a..75a08f5 100644 --- a/instruction-set.html +++ b/instruction-set.html @@ -3,7 +3,7 @@ Simple 8-bit Assembler - Instruction Set Help - + @@ -263,7 +263,7 @@

Other instructions

HLT
-

by Marco Schweighauser (2014) | MIT License

+

by Marco Schweighauser (2015) | MIT License

\ No newline at end of file diff --git a/package.json b/package.json index d93d159..46a0221 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "asmsimulator", - "version": "0.3.0", + "version": "0.4.0", "description": "Simple 8-bit Assembler Simulator in Javascript", "author": "Marco Schweighauser", "license": "MIT",