Skip to content

Instantly share code, notes, and snippets.

@DmitrySoshnikov
Last active June 15, 2018 20:38
Show Gist options
  • Save DmitrySoshnikov/6407781 to your computer and use it in GitHub Desktop.
Save DmitrySoshnikov/6407781 to your computer and use it in GitHub Desktop.
A simple educational Register-based VM, Assembler, and Disassembler.
// "Fetch, decode, eval!"
// A simple Register-based VM, Assembler, and Disassembler.
// by Dmitry Soshnikov <[email protected]>
// This virtual machine (VM) consists of registers (data storage),
// and operations (instructions) which operate on the registers.
// --------------------------------------------------------------
// Registers.
// --------------------------------------------------------------
// Storage: actual regs contents, initially empty (0).
var regStore = {ax: 0, bx: 0, cx: 0, dx: 0};
// Array to map register name to a number (index of the array), to be encoded
// in the instructions: ax: 0, bx: 1, etc.
var regs = Object.keys(regStore);
// --------------------------------------------------------------
// Instructions. Known as "opcodes" (operation codes).
// --------------------------------------------------------------
var instructionSet = {
halt: 0, // stops the program
mov : 1, // moves contents to a register
add : 2 // sums reg1 to reg2 and stores back in reg1
};
// --------------------------------------------------------------
// Assembler. Compiles mnemonics into a machine code.
// --------------------------------------------------------------
function assemble(program) {
return program.map(function(command) {
var
parsed = command.replace(',', '').split(/\s+/);
instruction = parsed[0],
op1 = parsed[1],
op2 = parsed[2];
// Each instruction is encoded as: [opcode, op1, op2], which in memory
// represented as HEX code 0x<opcode><op1><op2>, e.g. 0x1014 -- mov ax, 20.
var machineInstruction = [
instructionSet[instruction],
// In our instructions, firt op is always a register.
regs.indexOf(op1) > -1 ? regs.indexOf(op1) : 0,
// Second can be a register or an immediate value (stored as HEX).
regs.indexOf(op2) > -1 ? regs.indexOf(op2) :
op2 ? Number(op2).toString(16) : 0,
];
// Actual memory data, representing code in HEX.
memoryDump.push('0x' + machineInstruction.join('').toUpperCase());
return machineInstruction;
});
}
// --------------------------------------------------------------
// Decoder. Decodes instructions from memory.
// --------------------------------------------------------------
function decode(instruction) {
// In real machine, here goes decoding of encoded instruction number in
// memory. Here we have a simple version: [instr, reg, (reg|hex-value)].
return [
instruction[0],
instruction[1],
parseInt(instruction[2], 16)
];
}
// --------------------------------------------------------------
// Evaluating module of CPU.
// --------------------------------------------------------------
function eval(decoded) {
var
instruction = decoded[0],
op1 = decoded[1],
op2 = decoded[2];
switch (instruction) {
case 0: // halt, stop execution.
console.log('halt');
isRunning = false;
break;
case 1: // mov
regStore[regs[op1]] = op2;
// Debug disassembler at running.
console.log('mov ', regs[op1], ', ', op2);
break;
case 2: // add
regStore[regs[op1]] = regStore[regs[op1]] + regStore[regs[op2]];
console.log('add', regs[op1], ', ', regs[op2]);
break;
}
}
// --------------------------------------------------------------
// Fetcher.
// --------------------------------------------------------------
// Instructions pointer, points to the current command to be executed.
var ip = 0;
// Fetches the next command from the program (code segment in memory).
function fetch() {
return program[ip++];
}
// --------------------------------------------------------------
// Testing.
// --------------------------------------------------------------
// Test assembly program.
var assemblyCode = [
'mov ax, 10',
'mov bx, 20',
'add ax, bx',
'halt'
];
// Generate machine code.
var memoryDump = [];
var program = assemble(assemblyCode);
// Check generated machine code.
// ['0x10A', '0x1114', '0x201', '0x000']
console.log('Code segment:', memoryDump);
// CPU at work.
var isRunning = true;
while (isRunning) {
// Main execution loop: fetch, decode, eval.
var instruction = fetch();
var decoded = decode(instruction);
eval(decoded);
}
// Check registers contents after program execution.
// {ax: 30, bx: 20, cx: 0, dx: 0}
console.log('Registers:', regStore);
@bga
Copy link

bga commented Sep 4, 2013

overwriting eval heh. Looks wierd for js coder

@DmitrySoshnikov
Copy link
Author

In this case it's not about JS, it's about machine ;) JS here used as just a quick language for testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment