style(formatting): Small code format cleanup

This commit is contained in:
Schweigi 2015-03-15 22:23:36 -07:00
parent 6a043667bf
commit 1b549c25dc
18 changed files with 428 additions and 364 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
.idea
.DS_Store
node_modules/*
ASMSimulator.iml
ASMSimulator.iml

View File

@ -45,4 +45,4 @@ module.exports = function(grunt) {
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
};

View File

@ -1,20 +1,20 @@
var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes', function(opcodes) {
var app = angular.module('ASMSimulator', []);
;app.service('assembler', ['opcodes', function (opcodes) {
return {
go: function(input) {
var self = this;
go: function (input) {
// Use https://www.debuggex.com/
// Matches: "label: INSTRUCTION (["')OPERAND1(]"'), (["')OPERAND2(]"')
// GROUPS: 1 2 3 7
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 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*))?)?)?/;
// Regex group indexes for operands
var op1_group = 3;
var op2_group = 7;
var op1_group=3; // group indexes for operands
var op2_group=7;
// MATCHES: "(+|-)INTEGER"
var regexNum = /^[-+]?[0-9]+$/;
// MATCHES: "(.L)abel"
var regexLabel = /^[.A-Za-z]\w*$/;
// Contains the program code & data generated by the assembler
var code = [];
// Contains the mapping from instructions to assembler line
@ -24,26 +24,28 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
// Hash of uppercase labels used to detect duplicates
var normalizedLabels = {};
var lines = input.split('\n'); // Split text into code lines
// Split text into code lines
var lines = input.split('\n');
// Allowed formats: 200, 200d, 0xA4, 0o48, 101b
var parseNumber = function(input) {
if (input.slice(0,2) === "0x") {
var parseNumber = function (input) {
if (input.slice(0, 2) === "0x") {
return parseInt(input.slice(2), 16);
} else if (input.slice(0,2) === "0o") {
} else if (input.slice(0, 2) === "0o") {
return parseInt(input.slice(2), 8);
} else if (input.slice(input.length-1) === "b") {
return parseInt(input.slice(0, input.length-1), 2);
} else if (input.slice(input.length-1) === "d") {
return parseInt(input.slice(0, input.length-1), 10);
} else if (input.slice(input.length - 1) === "b") {
return parseInt(input.slice(0, input.length - 1), 2);
} else if (input.slice(input.length - 1) === "d") {
return parseInt(input.slice(0, input.length - 1), 10);
} else if (regexNum.exec(input)) {
return parseInt(input, 10);
} else {
throw "Invalid number format";
}
};
// Allowed registers: A, B, C, D, SP
var parseRegister = function(input) {
var parseRegister = function (input) {
input = input.toUpperCase();
if (input === 'A') {
@ -55,75 +57,75 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
} else if (input === 'D') {
return 3;
} else if (input === 'SP') {
return 4;
} else {
return 4;
} else {
return undefined;
}
};
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 code for register
};
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 code for register
};
// Allowed: Register, Label or Number; SP+/-Number is allowed for 'regaddress' type
var parseRegOrNumber = function(input, typeReg, typeNumber) {
var parseRegOrNumber = function (input, typeReg, typeNumber) {
var register = parseRegister(input);
if (register !== undefined) {
return { type: typeReg, value: register};
return {type: typeReg, value: register};
} else {
var label = parseLabel(input);
if (label !== undefined) {
return { type: typeNumber, value: label};
return {type: typeNumber, value: label};
} else {
if (typeReg === "regaddress") {
register = parseOffsetAddressing(input);
if (register !== undefined) {
return { type: typeReg, value: register};
}
}
if (typeReg === "regaddress") {
register = parseOffsetAddressing(input);
if (register !== undefined) {
return {type: typeReg, value: register};
}
}
var value = parseNumber(input);
if (isNaN(value)) {
@ -132,39 +134,41 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
else if (value < 0 || value > 255)
throw typeNumber + " must have a value between 0-255";
return { type: typeNumber, value: value};
return {type: typeNumber, value: value};
}
}
};
// Allowed: Label
var parseLabel = function(input) {
var parseLabel = function (input) {
return regexLabel.exec(input) ? input : undefined;
};
var getValue = function(input) {
switch(input.slice(0,1)) {
var getValue = function (input) {
switch (input.slice(0, 1)) {
case '[': // [number] or [register]
var address = input.slice(1,input.length-1);
var address = input.slice(1, input.length - 1);
return parseRegOrNumber(address, "regaddress", "address");
case '"': // "String"
var text = input.slice(1,input.length-1);
var text = input.slice(1, input.length - 1);
var chars = [];
for (var i = 0, l = text.length; i < l; i++) {
chars.push(text.charCodeAt(i));
}
return { type: "numbers", value: chars };
return {type: "numbers", value: chars};
case "'": // 'C'
var character = input.slice(1,input.length-1);
var character = input.slice(1, input.length - 1);
if (character.length > 1)
throw "Only one character is allowed. Use String instead";
return { type: "number", value: character.charCodeAt(0) };
return {type: "number", value: character.charCodeAt(0)};
default: // REGISTER, NUMBER or LABEL
return parseRegOrNumber(input, "register", "number");
}
};
var addLabel = function(label) {
var addLabel = function (label) {
var upperLabel = label.toUpperCase();
if (upperLabel in normalizedLabels)
throw "Duplicate label: " + label;
@ -174,14 +178,14 @@ 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++) {
var checkNoExtraArg = function (instr, arg) {
if (arg !== undefined) {
throw instr + ": too many arguments";
}
};
for (var i = 0, l = lines.length; i < l; i++) {
try {
var match = regex.exec(lines[i]);
if (match[1] !== undefined || match[2] !== undefined) {
@ -199,7 +203,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
mapping[code.length] = i;
}
switch(instr) {
switch (instr) {
case 'DB':
p1 = getValue(match[op1_group]);
@ -213,16 +217,16 @@ 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 'HLT':
checkNoExtraArg('HLT', match[op1_group]);
opCode = opcodes.NONE;
code.push(opCode);
break;
case 'MOV':
p1 = getValue(match[op1_group]);
p2 = getValue(match[op2_group]);
if (p1.type === "register" && p2.type === "register")
opCode = opcodes.MOV_REG_TO_REG;
else if (p1.type === "register" && p2.type === "address")
@ -280,7 +284,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'INC':
p1 = getValue(match[op1_group]);
checkNoExtraArg('INC',match[op2_group]);
checkNoExtraArg('INC', match[op2_group]);
if (p1.type === "register")
opCode = opcodes.INC_REG;
@ -292,7 +296,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'DEC':
p1 = getValue(match[op1_group]);
checkNoExtraArg('DEC',match[op2_group]);
checkNoExtraArg('DEC', match[op2_group]);
if (p1.type === "register")
opCode = opcodes.DEC_REG;
@ -321,7 +325,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'JMP':
p1 = getValue(match[op1_group]);
checkNoExtraArg('JMP',match[op2_group]);
checkNoExtraArg('JMP', match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JMP_REGADDRESS;
@ -332,9 +336,11 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'JC':case 'JB':case 'JNAE':
case 'JC':
case 'JB':
case 'JNAE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JC_REGADDRESS;
@ -345,9 +351,11 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'JNC':case 'JNB':case 'JAE':
case 'JNC':
case 'JNB':
case 'JAE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JNC_REGADDRESS;
@ -358,9 +366,10 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'JZ': case 'JE':
case 'JZ':
case 'JE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JZ_REGADDRESS;
@ -371,9 +380,10 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'JNZ': case 'JNE':
case 'JNZ':
case 'JNE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JNZ_REGADDRESS;
@ -384,9 +394,10 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'JA': case 'JNBE':
case 'JA':
case 'JNBE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JA_REGADDRESS;
@ -397,9 +408,10 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'JNA': case 'JBE':
case 'JNA':
case 'JBE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JNA_REGADDRESS;
@ -412,7 +424,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'PUSH':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.PUSH_REG;
@ -429,7 +441,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'POP':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.POP_REG;
@ -440,7 +452,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'CALL':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.CALL_REGADDRESS;
@ -452,16 +464,16 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'RET':
checkNoExtraArg(instr,match[op1_group]);
checkNoExtraArg(instr, match[op1_group]);
opCode = opcodes.RET;
code.push(opCode);
break;
case 'MUL':
case 'MUL':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.MUL_REG;
@ -478,7 +490,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'DIV':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.DIV_REG;
@ -546,7 +558,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
break;
case 'NOT':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.NOT_REG;
@ -555,7 +567,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value);
break;
case 'SHL':case 'SAL':
case 'SHL':
case 'SAL':
p1 = getValue(match[op1_group]);
p2 = getValue(match[op2_group]);
@ -572,7 +585,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
code.push(opCode, p1.value, p2.value);
break;
case 'SHR': case 'SAR':
case 'SHR':
case 'SAR':
p1 = getValue(match[op1_group]);
p2 = getValue(match[op2_group]);
@ -596,31 +610,32 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
} else {
// Check if line starts with a comment otherwise the line contains an error and can not be parsed
var line = lines[i].trim();
if (line !== "" && line.slice(0,1) !== ";") {
if (line !== "" && line.slice(0, 1) !== ";") {
throw "Syntax error";
}
}
} catch(e) {
throw { error: e, line: i};
} catch (e) {
throw {error: e, line: i};
}
}
// Replace label
for(i = 0, l = code.length; i < l; i++) {
for (i = 0, l = code.length; i < l; i++) {
if (!angular.isNumber(code[i])) {
if (code[i] in labels) {
code[i] = labels[code[i]];
} else {
throw { error: "Undefined label: " + code[i] };
throw {error: "Undefined label: " + code[i]};
}
}
}
return { code: code, mapping: mapping, labels: labels };
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;
@ -637,20 +652,24 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
return reg;
}
};
var checkGPR_SP = function(reg) {
if (reg < 0 || reg >= 1+self.gpr.length) {
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 <self.gpr.length) {
if(reg >= 0 && reg < self.gpr.length) {
self.gpr[reg] = value;
} else if(reg == self.gpr.length) {
self.sp=value;
if (self.sp < self.minSP) { // Not likely to happen, since we always get here after checkOpertion().
self.sp = value;
// Not likely to happen, since we always get here after checkOpertion().
if (self.sp < self.minSP) {
throw "Stack overflow";
} else if (self.sp > self.maxSP) {
throw "Stack underflow";
@ -659,9 +678,10 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
throw "Invalid register: " + reg;
}
};
var getGPR_SP = function(reg)
{
if(reg >= 0 && reg <self.gpr.length) {
if(reg >= 0 && reg < self.gpr.length) {
return self.gpr[reg];
} else if(reg == self.gpr.length) {
return self.sp;
@ -669,7 +689,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
throw "Invalid register: " + reg;
}
};
var indirectRegisterAddress=function(value) {
var indirectRegisterAddress = function(value) {
var reg = value % 8;
var base;
@ -680,12 +701,13 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
}
var offset = Math.floor(value / 8);
if ( offset>15 ) {
if ( offset > 15 ) {
offset = offset - 32;
}
return base+offset;
};
var checkOperation = function(value) {
self.zero = false;
self.carry = false;
@ -702,6 +724,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
return value;
};
var jump = function(newIP) {
if (newIP < 0 || newIP >= memory.data.length) {
throw "IP outside memory";
@ -709,12 +732,14 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
self.ip = newIP;
}
};
var push = function(value) {
memory.store(self.sp--, value);
if (self.sp < self.minSP) {
throw "Stack overflow";
}
};
var pop = function() {
var value = memory.load(++self.sp);
if (self.sp > self.maxSP) {
@ -723,6 +748,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
return value;
};
var division = function(divisor) {
if (divisor === 0) {
throw "Division by 0";
@ -1203,11 +1229,12 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
cpu.reset();
return cpu;
}]);;app.service('memory', [function() {
}]);
;app.service('memory', [function () {
var memory = {
data: Array(256),
lastAccess: -1,
load: function(address) {
load: function (address) {
var self = this;
if (address < 0 || address >= self.data.length) {
@ -1217,7 +1244,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
self.lastAccess = address;
return self.data[address];
},
store: function(address, value) {
store: function (address, value) {
var self = this;
if (address < 0 || address >= self.data.length) {
@ -1227,7 +1254,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
self.lastAccess = address;
self.data[address] = value;
},
reset: function() {
reset: function () {
var self = this;
self.lastAccess = -1;
@ -1239,7 +1266,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
memory.reset();
return memory;
}]);;app.service('opcodes', [function() {
}]);
;app.service('opcodes', [function() {
var opcodes = {
NONE: 0,
MOV_REG_TO_REG: 1,
@ -1318,7 +1346,8 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
};
return opcodes;
}]);;app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'assembler', function($document, $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 = '';
@ -1329,20 +1358,23 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
$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.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";
$scope.reset = function() {
$scope.reset = function () {
cpu.reset();
memory.reset();
$scope.error = '';
$scope.selectedLine = -1;
};
$scope.executeStep = function() {
$scope.executeStep = function () {
if (!$scope.checkPrgrmLoaded()) {
$scope.assemble();
}
@ -1364,13 +1396,13 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
};
var runner;
$scope.run = function() {
$scope.run = function () {
if (!$scope.checkPrgrmLoaded()) {
$scope.assemble();
}
$scope.isRunning = true;
runner = $timeout(function() {
runner = $timeout(function () {
if ($scope.executeStep() === true) {
$scope.run();
} else {
@ -1379,12 +1411,12 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
}, 1000 / $scope.speed);
};
$scope.stop = function() {
$scope.stop = function () {
$timeout.cancel(runner);
$scope.isRunning = false;
};
$scope.checkPrgrmLoaded = function() {
$scope.checkPrgrmLoaded = function () {
for (var i = 0, l = memory.data.length; i < l; i++) {
if (memory.data[i] !== 0) {
return true;
@ -1394,7 +1426,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
return false;
};
$scope.getChar = function(value) {
$scope.getChar = function (value) {
var text = String.fromCharCode(value);
if (text.trim() === '') {
@ -1404,14 +1436,14 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
}
};
$scope.assemble = function() {
$scope.assemble = function () {
try {
$scope.reset();
var assembly = assembler.go($scope.code);
$scope.mapping = assembly.mapping;
var binary = assembly.code;
$scope.labels = assembly.labels;
$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";
@ -1429,19 +1461,19 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
}
};
$scope.jumpToLine = function(index) {
$scope.jumpToLine = function (index) {
$document[0].getElementById('sourceCode').scrollIntoView();
$scope.selectedLine = $scope.mapping[index];
};
$scope.isInstruction = function(index) {
$scope.isInstruction = function (index) {
return $scope.mapping !== undefined &&
$scope.mapping[index] !== undefined &&
$scope.displayInstr;
$scope.mapping[index] !== undefined &&
$scope.displayInstr;
};
$scope.getMemoryCellCss = function(index) {
$scope.getMemoryCellCss = function (index) {
if (index >= $scope.outputStartIndex) {
return 'output-bg';
} else if ($scope.isInstruction(index)) {
@ -1453,7 +1485,7 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
}
};
$scope.getMemoryInnerCellCss = function(index) {
$scope.getMemoryInnerCellCss = function (index) {
if (index === cpu.ip) {
return 'marker marker-ip';
} else if (index === cpu.sp) {
@ -1470,11 +1502,13 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
return '';
}
};
}]);;app.filter('flag', function() {
}]);
;app.filter('flag', function() {
return function(input) {
return input.toString().toUpperCase();
};
});;app.filter('number', function() {
});
;app.filter('number', function() {
return function(input, isHex) {
if (isHex) {
var hex = input.toString(16).toUpperCase();
@ -1483,28 +1517,29 @@ var app = angular.module('ASMSimulator', []);;app.service('assembler', ['opcodes
return input.toString(10);
}
};
});;// Source: http://lostsource.com/2012/11/30/selecting-textarea-line.html
app.directive('selectLine', [function() {
});
;// Source: http://lostsource.com/2012/11/30/selecting-textarea-line.html
app.directive('selectLine', [function () {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
scope.$watch('selectedLine', function() {
link: function (scope, element, attrs, controller) {
scope.$watch('selectedLine', function () {
if (scope.selectedLine >= 0) {
var lines = element[0].value.split("\n");
// Calculate start/end
var startPos = 0;
for(var x = 0; x < lines.length; x++) {
if(x == scope.selectedLine) {
for (var x = 0; x < lines.length; x++) {
if (x == scope.selectedLine) {
break;
}
startPos += (lines[x].length+1);
startPos += (lines[x].length + 1);
}
var endPos = lines[scope.selectedLine].length+startPos;
var endPos = lines[scope.selectedLine].length + startPos;
// Chrome / Firefox
if(typeof(element[0].selectionStart) != "undefined") {
if (typeof(element[0].selectionStart) != "undefined") {
element[0].focus();
element[0].selectionStart = startPos;
element[0].selectionEnd = endPos;
@ -1524,15 +1559,17 @@ app.directive('selectLine', [function() {
});
}
};
}]);;app.filter('startFrom', function() {
}]);
;app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
};
});;app.directive('tabSupport', [function() {
});
;app.directive('tabSupport', [function () {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
link: function (scope, element, attrs, controller) {
element.bind("keydown", function (e) {
if (e.keyCode === 9) {
var val = this.value;
@ -1548,4 +1585,4 @@ app.directive('selectLine', [function() {
});
}
};
}]);
}]);

File diff suppressed because one or more lines are too long

View File

@ -59,4 +59,4 @@
.marker-d {
background-color: #E81F1F;
}
}

View File

@ -166,4 +166,4 @@
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<script src="assets/asmsimulator.js"></script>
</body>
</html>
</html>

View File

@ -266,4 +266,4 @@ HLT
<p><small>by Marco Schweighauser (2015) | MIT License</small></p>
</div>
</body>
</html>
</html>

View File

@ -1 +1 @@
var app = angular.module('ASMSimulator', []);
var app = angular.module('ASMSimulator', []);

View File

@ -1,20 +1,19 @@
app.service('assembler', ['opcodes', function(opcodes) {
app.service('assembler', ['opcodes', function (opcodes) {
return {
go: function(input) {
var self = this;
go: function (input) {
// Use https://www.debuggex.com/
// Matches: "label: INSTRUCTION (["')OPERAND1(]"'), (["')OPERAND2(]"')
// GROUPS: 1 2 3 7
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 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*))?)?)?/;
// Regex group indexes for operands
var op1_group = 3;
var op2_group = 7;
var op1_group=3; // group indexes for operands
var op2_group=7;
// MATCHES: "(+|-)INTEGER"
var regexNum = /^[-+]?[0-9]+$/;
// MATCHES: "(.L)abel"
var regexLabel = /^[.A-Za-z]\w*$/;
// Contains the program code & data generated by the assembler
var code = [];
// Contains the mapping from instructions to assembler line
@ -24,26 +23,28 @@ app.service('assembler', ['opcodes', function(opcodes) {
// Hash of uppercase labels used to detect duplicates
var normalizedLabels = {};
var lines = input.split('\n'); // Split text into code lines
// Split text into code lines
var lines = input.split('\n');
// Allowed formats: 200, 200d, 0xA4, 0o48, 101b
var parseNumber = function(input) {
if (input.slice(0,2) === "0x") {
var parseNumber = function (input) {
if (input.slice(0, 2) === "0x") {
return parseInt(input.slice(2), 16);
} else if (input.slice(0,2) === "0o") {
} else if (input.slice(0, 2) === "0o") {
return parseInt(input.slice(2), 8);
} else if (input.slice(input.length-1) === "b") {
return parseInt(input.slice(0, input.length-1), 2);
} else if (input.slice(input.length-1) === "d") {
return parseInt(input.slice(0, input.length-1), 10);
} else if (input.slice(input.length - 1) === "b") {
return parseInt(input.slice(0, input.length - 1), 2);
} else if (input.slice(input.length - 1) === "d") {
return parseInt(input.slice(0, input.length - 1), 10);
} else if (regexNum.exec(input)) {
return parseInt(input, 10);
} else {
throw "Invalid number format";
}
};
// Allowed registers: A, B, C, D, SP
var parseRegister = function(input) {
var parseRegister = function (input) {
input = input.toUpperCase();
if (input === 'A') {
@ -55,75 +56,75 @@ app.service('assembler', ['opcodes', function(opcodes) {
} else if (input === 'D') {
return 3;
} else if (input === 'SP') {
return 4;
} else {
return 4;
} else {
return undefined;
}
};
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 code for register
};
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 code for register
};
// Allowed: Register, Label or Number; SP+/-Number is allowed for 'regaddress' type
var parseRegOrNumber = function(input, typeReg, typeNumber) {
var parseRegOrNumber = function (input, typeReg, typeNumber) {
var register = parseRegister(input);
if (register !== undefined) {
return { type: typeReg, value: register};
return {type: typeReg, value: register};
} else {
var label = parseLabel(input);
if (label !== undefined) {
return { type: typeNumber, value: label};
return {type: typeNumber, value: label};
} else {
if (typeReg === "regaddress") {
register = parseOffsetAddressing(input);
if (register !== undefined) {
return { type: typeReg, value: register};
}
}
if (typeReg === "regaddress") {
register = parseOffsetAddressing(input);
if (register !== undefined) {
return {type: typeReg, value: register};
}
}
var value = parseNumber(input);
if (isNaN(value)) {
@ -132,39 +133,41 @@ app.service('assembler', ['opcodes', function(opcodes) {
else if (value < 0 || value > 255)
throw typeNumber + " must have a value between 0-255";
return { type: typeNumber, value: value};
return {type: typeNumber, value: value};
}
}
};
// Allowed: Label
var parseLabel = function(input) {
var parseLabel = function (input) {
return regexLabel.exec(input) ? input : undefined;
};
var getValue = function(input) {
switch(input.slice(0,1)) {
var getValue = function (input) {
switch (input.slice(0, 1)) {
case '[': // [number] or [register]
var address = input.slice(1,input.length-1);
var address = input.slice(1, input.length - 1);
return parseRegOrNumber(address, "regaddress", "address");
case '"': // "String"
var text = input.slice(1,input.length-1);
var text = input.slice(1, input.length - 1);
var chars = [];
for (var i = 0, l = text.length; i < l; i++) {
chars.push(text.charCodeAt(i));
}
return { type: "numbers", value: chars };
return {type: "numbers", value: chars};
case "'": // 'C'
var character = input.slice(1,input.length-1);
var character = input.slice(1, input.length - 1);
if (character.length > 1)
throw "Only one character is allowed. Use String instead";
return { type: "number", value: character.charCodeAt(0) };
return {type: "number", value: character.charCodeAt(0)};
default: // REGISTER, NUMBER or LABEL
return parseRegOrNumber(input, "register", "number");
}
};
var addLabel = function(label) {
var addLabel = function (label) {
var upperLabel = label.toUpperCase();
if (upperLabel in normalizedLabels)
throw "Duplicate label: " + label;
@ -174,14 +177,14 @@ app.service('assembler', ['opcodes', function(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++) {
var checkNoExtraArg = function (instr, arg) {
if (arg !== undefined) {
throw instr + ": too many arguments";
}
};
for (var i = 0, l = lines.length; i < l; i++) {
try {
var match = regex.exec(lines[i]);
if (match[1] !== undefined || match[2] !== undefined) {
@ -199,7 +202,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
mapping[code.length] = i;
}
switch(instr) {
switch (instr) {
case 'DB':
p1 = getValue(match[op1_group]);
@ -213,16 +216,16 @@ app.service('assembler', ['opcodes', function(opcodes) {
throw "DB does not support this operand";
break;
case 'HLT':
checkNoExtraArg('HLT',match[op1_group]);
opCode=opcodes.NONE;
code.push(opCode);
break;
case 'HLT':
checkNoExtraArg('HLT', match[op1_group]);
opCode = opcodes.NONE;
code.push(opCode);
break;
case 'MOV':
p1 = getValue(match[op1_group]);
p2 = getValue(match[op2_group]);
if (p1.type === "register" && p2.type === "register")
opCode = opcodes.MOV_REG_TO_REG;
else if (p1.type === "register" && p2.type === "address")
@ -280,7 +283,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'INC':
p1 = getValue(match[op1_group]);
checkNoExtraArg('INC',match[op2_group]);
checkNoExtraArg('INC', match[op2_group]);
if (p1.type === "register")
opCode = opcodes.INC_REG;
@ -292,7 +295,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'DEC':
p1 = getValue(match[op1_group]);
checkNoExtraArg('DEC',match[op2_group]);
checkNoExtraArg('DEC', match[op2_group]);
if (p1.type === "register")
opCode = opcodes.DEC_REG;
@ -321,7 +324,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'JMP':
p1 = getValue(match[op1_group]);
checkNoExtraArg('JMP',match[op2_group]);
checkNoExtraArg('JMP', match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JMP_REGADDRESS;
@ -332,9 +335,11 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'JC':case 'JB':case 'JNAE':
case 'JC':
case 'JB':
case 'JNAE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JC_REGADDRESS;
@ -345,9 +350,11 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'JNC':case 'JNB':case 'JAE':
case 'JNC':
case 'JNB':
case 'JAE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JNC_REGADDRESS;
@ -358,9 +365,10 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'JZ': case 'JE':
case 'JZ':
case 'JE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JZ_REGADDRESS;
@ -371,9 +379,10 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'JNZ': case 'JNE':
case 'JNZ':
case 'JNE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JNZ_REGADDRESS;
@ -384,9 +393,10 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'JA': case 'JNBE':
case 'JA':
case 'JNBE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JA_REGADDRESS;
@ -397,9 +407,10 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'JNA': case 'JBE':
case 'JNA':
case 'JBE':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.JNA_REGADDRESS;
@ -412,7 +423,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'PUSH':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.PUSH_REG;
@ -429,7 +440,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'POP':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.POP_REG;
@ -440,7 +451,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'CALL':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.CALL_REGADDRESS;
@ -452,16 +463,16 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'RET':
checkNoExtraArg(instr,match[op1_group]);
checkNoExtraArg(instr, match[op1_group]);
opCode = opcodes.RET;
code.push(opCode);
break;
case 'MUL':
case 'MUL':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.MUL_REG;
@ -478,7 +489,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'DIV':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.DIV_REG;
@ -546,7 +557,7 @@ app.service('assembler', ['opcodes', function(opcodes) {
break;
case 'NOT':
p1 = getValue(match[op1_group]);
checkNoExtraArg(instr,match[op2_group]);
checkNoExtraArg(instr, match[op2_group]);
if (p1.type === "register")
opCode = opcodes.NOT_REG;
@ -555,7 +566,8 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value);
break;
case 'SHL':case 'SAL':
case 'SHL':
case 'SAL':
p1 = getValue(match[op1_group]);
p2 = getValue(match[op2_group]);
@ -572,7 +584,8 @@ app.service('assembler', ['opcodes', function(opcodes) {
code.push(opCode, p1.value, p2.value);
break;
case 'SHR': case 'SAR':
case 'SHR':
case 'SAR':
p1 = getValue(match[op1_group]);
p2 = getValue(match[op2_group]);
@ -596,28 +609,28 @@ app.service('assembler', ['opcodes', function(opcodes) {
} else {
// Check if line starts with a comment otherwise the line contains an error and can not be parsed
var line = lines[i].trim();
if (line !== "" && line.slice(0,1) !== ";") {
if (line !== "" && line.slice(0, 1) !== ";") {
throw "Syntax error";
}
}
} catch(e) {
throw { error: e, line: i};
} catch (e) {
throw {error: e, line: i};
}
}
// Replace label
for(i = 0, l = code.length; i < l; i++) {
for (i = 0, l = code.length; i < l; i++) {
if (!angular.isNumber(code[i])) {
if (code[i] in labels) {
code[i] = labels[code[i]];
} else {
throw { error: "Undefined label: " + code[i] };
throw {error: "Undefined label: " + code[i]};
}
}
}
return { code: code, mapping: mapping, labels: labels };
return {code: code, mapping: mapping, labels: labels};
}
};
}]);
}]);

View File

@ -15,20 +15,24 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
return reg;
}
};
var checkGPR_SP = function(reg) {
if (reg < 0 || reg >= 1+self.gpr.length) {
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 <self.gpr.length) {
if(reg >= 0 && reg < self.gpr.length) {
self.gpr[reg] = value;
} else if(reg == self.gpr.length) {
self.sp=value;
if (self.sp < self.minSP) { // Not likely to happen, since we always get here after checkOpertion().
self.sp = value;
// Not likely to happen, since we always get here after checkOpertion().
if (self.sp < self.minSP) {
throw "Stack overflow";
} else if (self.sp > self.maxSP) {
throw "Stack underflow";
@ -37,9 +41,10 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
throw "Invalid register: " + reg;
}
};
var getGPR_SP = function(reg)
{
if(reg >= 0 && reg <self.gpr.length) {
if(reg >= 0 && reg < self.gpr.length) {
return self.gpr[reg];
} else if(reg == self.gpr.length) {
return self.sp;
@ -47,7 +52,8 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
throw "Invalid register: " + reg;
}
};
var indirectRegisterAddress=function(value) {
var indirectRegisterAddress = function(value) {
var reg = value % 8;
var base;
@ -58,12 +64,13 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
}
var offset = Math.floor(value / 8);
if ( offset>15 ) {
if ( offset > 15 ) {
offset = offset - 32;
}
return base+offset;
};
var checkOperation = function(value) {
self.zero = false;
self.carry = false;
@ -80,6 +87,7 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
return value;
};
var jump = function(newIP) {
if (newIP < 0 || newIP >= memory.data.length) {
throw "IP outside memory";
@ -87,12 +95,14 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
self.ip = newIP;
}
};
var push = function(value) {
memory.store(self.sp--, value);
if (self.sp < self.minSP) {
throw "Stack overflow";
}
};
var pop = function() {
var value = memory.load(++self.sp);
if (self.sp > self.maxSP) {
@ -101,6 +111,7 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
return value;
};
var division = function(divisor) {
if (divisor === 0) {
throw "Division by 0";
@ -581,4 +592,4 @@ app.service('cpu', ['opcodes', 'memory', function(opcodes, memory) {
cpu.reset();
return cpu;
}]);
}]);

View File

@ -1,8 +1,8 @@
app.service('memory', [function() {
app.service('memory', [function () {
var memory = {
data: Array(256),
lastAccess: -1,
load: function(address) {
load: function (address) {
var self = this;
if (address < 0 || address >= self.data.length) {
@ -12,7 +12,7 @@ app.service('memory', [function() {
self.lastAccess = address;
return self.data[address];
},
store: function(address, value) {
store: function (address, value) {
var self = this;
if (address < 0 || address >= self.data.length) {
@ -22,7 +22,7 @@ app.service('memory', [function() {
self.lastAccess = address;
self.data[address] = value;
},
reset: function() {
reset: function () {
var self = this;
self.lastAccess = -1;
@ -34,4 +34,4 @@ app.service('memory', [function() {
memory.reset();
return memory;
}]);
}]);

View File

@ -77,4 +77,4 @@ app.service('opcodes', [function() {
};
return opcodes;
}]);
}]);

View File

@ -1,4 +1,4 @@
app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'assembler', function($document, $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 = '';
@ -9,20 +9,23 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
$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.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";
$scope.reset = function() {
$scope.reset = function () {
cpu.reset();
memory.reset();
$scope.error = '';
$scope.selectedLine = -1;
};
$scope.executeStep = function() {
$scope.executeStep = function () {
if (!$scope.checkPrgrmLoaded()) {
$scope.assemble();
}
@ -44,13 +47,13 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
};
var runner;
$scope.run = function() {
$scope.run = function () {
if (!$scope.checkPrgrmLoaded()) {
$scope.assemble();
}
$scope.isRunning = true;
runner = $timeout(function() {
runner = $timeout(function () {
if ($scope.executeStep() === true) {
$scope.run();
} else {
@ -59,12 +62,12 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
}, 1000 / $scope.speed);
};
$scope.stop = function() {
$scope.stop = function () {
$timeout.cancel(runner);
$scope.isRunning = false;
};
$scope.checkPrgrmLoaded = function() {
$scope.checkPrgrmLoaded = function () {
for (var i = 0, l = memory.data.length; i < l; i++) {
if (memory.data[i] !== 0) {
return true;
@ -74,7 +77,7 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
return false;
};
$scope.getChar = function(value) {
$scope.getChar = function (value) {
var text = String.fromCharCode(value);
if (text.trim() === '') {
@ -84,14 +87,14 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
}
};
$scope.assemble = function() {
$scope.assemble = function () {
try {
$scope.reset();
var assembly = assembler.go($scope.code);
$scope.mapping = assembly.mapping;
var binary = assembly.code;
$scope.labels = assembly.labels;
$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";
@ -109,19 +112,19 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
}
};
$scope.jumpToLine = function(index) {
$scope.jumpToLine = function (index) {
$document[0].getElementById('sourceCode').scrollIntoView();
$scope.selectedLine = $scope.mapping[index];
};
$scope.isInstruction = function(index) {
$scope.isInstruction = function (index) {
return $scope.mapping !== undefined &&
$scope.mapping[index] !== undefined &&
$scope.displayInstr;
$scope.mapping[index] !== undefined &&
$scope.displayInstr;
};
$scope.getMemoryCellCss = function(index) {
$scope.getMemoryCellCss = function (index) {
if (index >= $scope.outputStartIndex) {
return 'output-bg';
} else if ($scope.isInstruction(index)) {
@ -133,7 +136,7 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
}
};
$scope.getMemoryInnerCellCss = function(index) {
$scope.getMemoryInnerCellCss = function (index) {
if (index === cpu.ip) {
return 'marker marker-ip';
} else if (index === cpu.sp) {
@ -150,4 +153,4 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
return '';
}
};
}]);
}]);

View File

@ -2,4 +2,4 @@ app.filter('flag', function() {
return function(input) {
return input.toString().toUpperCase();
};
});
});

View File

@ -7,4 +7,4 @@ app.filter('number', function() {
return input.toString(10);
}
};
});
});

View File

@ -1,25 +1,25 @@
// Source: http://lostsource.com/2012/11/30/selecting-textarea-line.html
app.directive('selectLine', [function() {
app.directive('selectLine', [function () {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
scope.$watch('selectedLine', function() {
link: function (scope, element, attrs, controller) {
scope.$watch('selectedLine', function () {
if (scope.selectedLine >= 0) {
var lines = element[0].value.split("\n");
// Calculate start/end
var startPos = 0;
for(var x = 0; x < lines.length; x++) {
if(x == scope.selectedLine) {
for (var x = 0; x < lines.length; x++) {
if (x == scope.selectedLine) {
break;
}
startPos += (lines[x].length+1);
startPos += (lines[x].length + 1);
}
var endPos = lines[scope.selectedLine].length+startPos;
var endPos = lines[scope.selectedLine].length + startPos;
// Chrome / Firefox
if(typeof(element[0].selectionStart) != "undefined") {
if (typeof(element[0].selectionStart) != "undefined") {
element[0].focus();
element[0].selectionStart = startPos;
element[0].selectionEnd = endPos;
@ -39,4 +39,4 @@ app.directive('selectLine', [function() {
});
}
};
}]);
}]);

View File

@ -3,4 +3,4 @@ app.filter('startFrom', function() {
start = +start; //parse to int
return input.slice(start);
};
});
});

View File

@ -1,7 +1,7 @@
app.directive('tabSupport', [function() {
app.directive('tabSupport', [function () {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
link: function (scope, element, attrs, controller) {
element.bind("keydown", function (e) {
if (e.keyCode === 9) {
var val = this.value;
@ -17,4 +17,4 @@ app.directive('tabSupport', [function() {
});
}
};
}]);
}]);