Skip to content

Instantly share code, notes, and snippets.

@JayTailor45
Last active March 26, 2021 05:24
Show Gist options
  • Save JayTailor45/da83e2c4e160fb523d30aa7ab349461d to your computer and use it in GitHub Desktop.
Save JayTailor45/da83e2c4e160fb523d30aa7ab349461d to your computer and use it in GitHub Desktop.
Assembly instructions interpreter challenge
/*
We want to create a simple interpreter of assembler which will support the following instructions:
`mov x y` - copies y (either a constant value or the content of a register) into register x
`inc x` - increases the content of the register x by one
`dec x` - decreases the content of the register x by one
`jnz x y` - jumps to an instruction y steps away (positive means forward, negative means backward, y can be a register or a constant), but only if x (a constant or a register) is not zero
Register names are alphabetical (letters only). Constants are always integers (positive or negative).
Note: the jnz instruction moves relative to itself. For example, an offset of -1 would continue at the previous instruction, while an offset of 2 would skip over the next instruction.
The function will take an input list with the sequence of the program instructions and will execute them. The program ends when there are no more instructions to execute, then it returns a dictionary with the contents of the registers.
Also, every inc/dec/jnz on a register will always be preceeded by a mov on the register first, so you don't need to worry about uninitialized registers.
```js
interpret(["mov a 1"]) // => {a: 1}
interpret(["mov a 2", "inc a"]) // => {a: 3}
interpret(["mov a 1", "mov b a", "dec b"]) // => {a:1, b:0}
interpret( ["mov a 5","inc a","dec a","dec a","jnz a -1","inc a"] ) // => {a: 1}
interpret( ["mov a 5","mov b a","dec b","dec b"] ) // => {a: 5, b: 3}
interpret( ["mov a 1", "jnz 5 2", "mov b 3", "inc a"] ) // => {a: 2}
```
Add support for the following instructions.
`add x y` - add the content of the register x with y (either an integer or the value of a register) and stores the result in x (i.e. register[x] += y).
`sub x y` - subtract y (either an integer or the value of a register) from the register x and stores the result in x (i.e. register[x] -= y).
`mul x y` - same with multiply
`div x y` - same with integer division
```js
interpret(["mov a 2", "add a 10",]) // => {a:12}
interpret(["mov a 2", "mov b 3", "add a b"]) // => {a:5, b:3}
interpret(["mov a 20", "sub a 5",]) // => {a:15}
interpret(["mov a 7", "mov b 2", "sub a b"]) // => {a:5, b:2}
interpret(["mov a 20", "mul a 5",]) // => {a:100}
interpret(["mov a 10", "mov b 5", "mul a b"]) // => {a:50, b:5}
interpret(["mov a 20", "div a 5",]) // => {a:4}
interpret(["mov a 6", "mov b 3", "div a b"]) // => {a:2, b:3}
```
If the function should receive invalid instructions, it should return an object containing which line the error occured, and an error message.
If an instruction is correctly formed but the operation is impossible (e.g ["mov a 1", "inc b"]), it should return an object containing which line the error occured, and an error message.
If the function is stuck in an infinite loop, it should , it should return an object containing which line the error occured, and an error message.
Add support for the following instructions
`label:` - define a label position (label = identifier + ":", an identifier being a string that does not match any other command). Jump commands and call are aimed to these labels positions in the program.
`jmp lbl` - jumps to the label lbl.
`cmp x y` - compares x (either an integer or the value of a register) and y (either an integer or the value of a register). The result is used in the conditional jumps (jne, je, jge, jg, jle and jl)
`jne lbl` - jump to the label lbl if the values of the previous cmp command were not equal.
`je lbl` - jump to the label lbl if the values of the previous cmp command were equal.
`jge lbl` - jump to the label lbl if x was greater or equal than y in the previous cmp command.
`jg lbl` - jump to the label lbl if x was greater than y in the previous cmp command.
`jle lbl` - jump to the label lbl if x was less or equal than y in the previous cmp command.
`jl lbl` - jump to the label lbl if x was less than y in the previous cmp command.
```js
interpret(["mov a 10", "start:", "dec a", "cmp a 0", "jne start"])
```
*/
class Interpreter {
registers = {};
pc = 0;
lableArr = [];
REG_cmpX;
REG_cmpY;
constructor() {
this.instructions= {
mov: this.mov,
inc: this.inc,
dec: this.dec,
jnz: this.jnz,
add: this.add,
sub: this.sub,
mul: this.mul,
div: this.div,
jmp: this.jmp,
cmp: this.cmp,
jne: this.jne,
je: this.je,
jge: this.jge,
jg: this.jg,
jle: this.jle,
jl: this.jl
};
}
interpret = (program) => {
const INSTRUCTION_LIMIT = 1000;
let counter = 0;
console.log(program);
try {
while(program.length > this.pc) {
if(program[this.pc].includes(':')) {
this.lableArr.push({name: program[this.pc], location: this.pc});
} else {
this.parse(program[this.pc]);
}
this.pc++;
counter++;
if(counter > INSTRUCTION_LIMIT) throw 'Instructions limit exceed'
}
} catch(e) {
console.error(e);
}
console.table(this.registers)
}
parse = (current) => {
const instructionArr = current.split(' ');
const instruction = instructionArr[0];
const arg1 = instructionArr[1];
const arg2 = instructionArr[2];
this.instructions[instruction](arg1, arg2);
}
fetchReg = (arg) => {
const isExists = this.registers ? Object.keys(this.registers).some(reg => reg === arg) : false;
return isExists ? this.registers[arg] : Number.parseInt(arg);
}
mov = (arg1, arg2) => {
this.registers[arg1] = this.fetchReg(arg2);
}
inc = (arg) => {
if(this.registers[arg]) {
this.registers[arg]++;
} else {
console.error(`Invalid Instruction!`);
console.trace();
}
}
dec = (arg) => {
if(this.registers[arg]) {
this.registers[arg]--;
} else {
console.error(`Invalid Instruction!`);
console.trace();
}
}
jnz = (arg1, arg2) => {
if(this.fetchReg(arg1) != 0) {
this.pc += this.fetchReg(arg2) -1;
}
}
add = (arg1, arg2) => {
this.registers[arg1] += this.fetchReg(arg2);
}
sub = (arg1, arg2) => {
this.registers[arg1] -= this.fetchReg(arg2);
}
mul = (arg1, arg2) => {
this.registers[arg1] *= this.fetchReg(arg2);
}
div = (arg1, arg2) => {
this.registers[arg1] /= this.fetchReg(arg2);
}
jmp = (arg) => {
this.lableArr.forEach(labelObj => {
if(labelObj.name.includes(arg)) {
this.pc = labelObj.location;
}
});
}
cmp = (arg1, arg2) => {
this.REG_cmpX = this.fetchReg(arg1);
this.REG_cmpY = this.fetchReg(arg2);
}
jne = (arg) => {
if(this.REG_cmpX !== this.REG_cmpY) {
this.jmp(arg);
}
}
je = (arg) => {
if(this.REG_cmpX === this.REG_cmpY) {
this.jmp(arg);
}
}
jge = (arg) => {
if(this.REG_cmpX >= this.REG_cmpY) {
this.jmp(arg);
}
}
jg = (arg) => {
if(this.REG_cmpX > this.REG_cmpY) {
this.jmp(arg);
}
}
jle = (arg) => {
if(this.REG_cmpX <= this.REG_cmpY) {
this.jmp(arg);
}
}
jl = (arg) => {
if(this.REG_cmpX < this.REG_cmpY) {
this.jmp(arg);
}
}
}
// =====================================================================================
const assemblyInterpreter = new Interpreter();
//assemblyInterpreter.interpret(["mov a 1"]) // => {a: 1}
//assemblyInterpreter.interpret(["mov a 2", "inc a"]) // => {a: 3}
//assemblyInterpreter.interpret(["mov a 1", "mov b a", "dec b"]) // => {a:1, b:0}
//assemblyInterpreter.interpret( ["mov a 5","inc a","dec a","dec a","jnz a -1","inc a"] ) // => {a: 1}
//assemblyInterpreter.interpret( ["mov a 5","mov b a","dec b","dec b"] ) // => {a: 5, b: 3}
//assemblyInterpreter.interpret( ["mov a 1", "jnz 5 2", "mov b 3", "inc a"] ) // => {a: 2}
// assemblyInterpreter.interpret(["mov a 2", "add a 10",]) // => {a:12}
// assemblyInterpreter.interpret(["mov a 2", "mov b 3", "add a b"]) // => {a:5, b:3}
// assemblyInterpreter.interpret(["mov a 20", "sub a 5",]) // => {a:15}
// assemblyInterpreter.interpret(["mov a 7", "mov b 2", "sub a b"]) // => {a:5, b:2}
// assemblyInterpreter.interpret(["mov a 20", "mul a 5",]) // => {a:100}
// assemblyInterpreter.interpret(["mov a 10", "mov b 5", "mul a b"]) // => {a:50, b:5}
// assemblyInterpreter.interpret(["mov a 20", "div a 5",]) // => {a:4}
// assemblyInterpreter.interpret(["mov a 6", "mov b 3", "div a b"]) // => {a:2, b:3}
// assemblyInterpreter.interpret(["mov a 1", "inc b"]) // => "Invalid Instruction"
assemblyInterpreter.interpret(["mov a 10", "start:", "dec a", "cmp a 0", "jne start"]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment