1.0.4 • Published 3 years ago

malbolge-vm v1.0.4

Weekly downloads
15
License
MIT
Repository
github
Last release
3 years ago

malbolge-vm

malbolge-vm is a node library to deal with Malbolge code.

This library let's you:

  • Validate Malbolge code
  • Normalize / Assemble Malbolge code
  • Execute Malbolge code step by step with user input support. You can also see the internal registers and memory.
  • Dynamically append and execute code (providing you've not accessed the default memory)

It also includes a CLI interpreter with input support if you install it globally.

Installation

To use it in your project:

npm install --save malbolge-vm

If you want to use the malbolge-interpreter command, you can install this globally:

npm install -g malbolge-vm

Then you will be able to run Malbolge programs via command line:

malbolge-interpreter some_file.mb

Usage

Basic code execution

var mb = require('malbolge-vm');
var code = '(&%$_""7<5492V0TA3P';

//First of all, we create new Malbolge VM, by using the load function
var vm = mb.load(code);

//Then that object gives us the ability to access registers and memory:
console.log('A register:', vm.a);
console.log('C register:', vm.c);
console.log('D register:', vm.d);
console.log('Memory @', 40, ':', vm.mem[40]);

var result = '';

try {
  //For simple code execution, use the exec function
  //exec function executes the VM and returns the output
  result = mb.exec(vm);
} catch (e) {
  //When it fails, it will throw an error message
  //or WANTS_INPUT if it would wait for user input
  if (e === mb.WANTS_INPUT)
    console.log('Error: User input expected.');
  else
    console.log('Error: '+e);
}

console.log('Output: '+result);

Step by step code execution with user input

var mb = require('malbolge-vm');
var code = 'ubs`M';

var vm = mb.load(code);

var result = '';
var temp;

//To execute a single step use the step function
//mb.step(vm, [input])
//This normally returns output character code or null
//This can also return EXIT, or throw WANTS_INPUT

while (true) {
  try {
    //When program finishes, step returns EXIT 
    while ((temp = mb.step(vm)) !== mb.EXIT) {
      if (temp !== null)
        result += String.fromCharCode(temp);
    }
    break;
  } catch (e) {
    //step throws WANTS_INPUT when it encounters input instruction
    //and no input is provided
    if (e === mb.WANTS_INPUT) {
      //step function optionally accepts user input - a number
      //use 10 for new line and 59048 for EOF
      mb.step(vm, 51); //assume user wanted to press '3'
      continue;
    }
    console.log('Error:', e);
  }
  break;
}
console.log('Output: '+result);

Code operations

var mb = require('malbolge-vm');
var code = '(&%$_""7<5492V0TA3P';
var position = 3;

//Normalize Malbolge code
var normalized = mb.normalize(code);
console.log('Normalized code', normalized);

//Assemble the code back
var assembled = mb.assemble(normalized);
console.log('assembled === code', assembled === code); //true

//Validate code
console.log('Is code valid?', mb.validate(code));
//For validation of normalized code, set the second parameter to true
console.log('Is normalized code valid?', mb.validate(normalized, true));

//To normalize a single instruction
var op = mb.decode(code[position], position);
console.log('Decoded op @', position, ':', op);

//Alternatively you could use decodeInt
op = mb.decodeInt(code.charCodeAt(position), position);
console.log('Decoded op @', position, ':', op);

//When you have a char of the normalized code,
//you can get the op code via assembly dictionary
console.log('Op code @', position, ':', mb.assembly[op]); //instruction 

//If you want to encode an instruction, use encode:
console.log('Halt @', position, 'is', mb.encode(mb.opc.halt, position));
console.log('Char code of halt @', position, 'is',
  mb.encodeInt(mb.opc.halt, position));

/*
Opcodes in Malbolge:

var opc = {
  jump: 4,
  out: 5,
  in : 23,
  rot: 39,
  movd: 40,
  opr: 62,
  nop: 68,
  halt: 81
};
*/

Dynamic code execution (advanced)

If you'd like to generate Malbolge code dynamically, this library can help you. Note that this topic is advanced - you will not be able to execute code that uses filled memory locations here.

var mb = require('malbolge-vm');
var code = 'D'; //NOP instruction

//You can disable filling memory by setting the optional 'partially' argument to true
var vm = mb.load(code, true);
//vm.mem.length is now 1, and we can append more instruction

//but before appending, let's execute the existing part of the code
mb.exec(vm);

//add another NOP, append to code and execute in VM
//appendAndPerform returns the char to be appended to code
code += mb.appendAndPerform(vm, mb.opc.nop);

//now you can analyse vm.a, vm.c, vm.d and vm.mem etc

//add input instruction, and execute as if user pressed enter
code += mb.appendAndPerform(vm, mb.opc.in, 10);
//vm.a is now 10

//now add jump instruction, but do NOT execute
code += mb.appendAndPerform(vm, mb.opc.jump, null, true); //skip set to true

//store the address of the instruction to be executed
var jumpLocation = vm.mem[vm.d] + 1; 

//now we have to fill the remaining memory

//fill the memory with NOP's
while(code.length < jumpLocation) {
  code += mb.appendAndPerform(vm, mb.opc.nop, null, true);
}

//now we can finally jump (vm.c is still 3)
mb.step(vm);

//and NOW we can append the instruction to be executed.
//without skip, because this time we execute it.
code += mb.appendAndPerform(vm, mb.opc.out);

//and finally - halt
code += mb.appendAndPerform(vm, mb.opc.halt);

console.log('Code: ', code);