UNPKG

ktro

Version:

KTRO is a virtual machine designed to execute `.pkg` files containing bytecode instructions. It has been created primarily to execute programs written in the KTRO programming language, which compiles down to its bytecode. While currently operating as an 8

1,286 lines (1,135 loc) 30.5 kB
/* The KTRO Virtual Machine This machine can compile KTRO Packages to run with JS. * PROJECT WILL BE FINISHED & READY IN 2024 * Todo: - Add support for 32 and 64 bit floats, - Ability to call standard library functions - Support some Javascript libs like WebGL, Express, etc As of now, only use bin type 0xA2 as its the only supported bin type. */ import * as fs from "fs"; export const PackageProgram = (output, program) => { return fs.writeFileSync(output, Buffer.from(program)); }; export const FromPackage = (p) => { try { const buffer = fs.readFileSync(p); return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.length); } catch (error) { // Handle read file error console.error("Error reading file:", error); return null; } }; export const chr = (c) => c.charCodeAt(0); export const VirtualMemoryBuffer = (sizeInBytes) => { const ab = new ArrayBuffer(sizeInBytes); const dv = new DataView(ab); return dv; }; export const imm16 = (word) => [(word >> 8) & 0xff, word & 0xff]; export const imm32 = (dword) => [ (dword >>> 24) & 0xff, (dword >>> 16) & 0xff, (dword >>> 8) & 0xff, dword & 0xff ]; export const SECTION = 0xfa; export const STEP_DEBUGGER = 0x0a; export const BRANCH_DEBUGGER = 0x1a; export const INST_DEBUGGER = 0x2a; export const NOP = 0x00; export const BINARY = 0x01; export const INTERRUPT = 0x02; export const META = 0x03; export const DB = 0x04; export const ERASE = 0x05; export const FUNC = 0x06; export const LABEL = 0x07; export const CONST = 0x10; export const ADD = 0x11; export const HLT = 0x12; export const CMPEQ = 0x13; export const CMPNE = 0x14; export const CMPGT = 0x15; export const CMPLT = 0x16; export const CMPGE = 0x17; export const CMPLE = 0x18; export const ASCIZ = 0x19; export const EXTERN = 0x20; export const STO = 0x0a; export const LDS = 0x1a; export const POPL = 0x2a; export const DEL = 0x3a; export const DUP = 0x4a; export const LDR = 0x6a; export const LDD = 0x7a; export const SF = 0x8a; export const CSF = 0x9a; export const SUB = 0xaa; export const MUL = 0xba; export const DIV = 0xca; export const FLR = 0xda; export const CEIL = 0xea; export const RDIV = 0xfa; export const INC = 0x0b; export const DEC = 0x1b; export const MOD = 0x2b; export const POW = 0x3b; export const SQRT = 0x4b; export const JMP = 0x5b; export const JE = 0x6b; export const JNE = 0x7b; export const JZE = 0x8b; export const CZF = 0x9b; export const CAF = 0xab; export const OFFSET = 0xbb; export const AND = 0xcb; export const OR = 0xdb; export const SHL = 0xec; export const SHR = 0xfc; export const XOR = 0x0c; export const NOT = 0x1c; export const NOR = 0x2c; export const CALL = 0x4c; export const RET = 0x5c; export const STD = 0x6c; export const U8 = 8; export const U16 = 16; export const U32 = 32; export const Integer8 = U8; export const Integer16 = U16; export const Integer32 = U32; export const VOID = 0; export const SYSINT = { EXIT: 0, WRITE: 1, READ: 2, }; export const StandardNamespace = { namespaceID: 0, jsFunctions: [ (kvm) => { let offset = kvm.getArgument(Integer32); let buf = readlineSync.question(""); kvm.storeStringToHeap(offset, buf); return kvm.resultValue(buf.length, Integer32); }, ] }; export const VirtualMemoryBufferAuto = () => { return VirtualMemoryBuffer(1024 * 1024); } export class KtroPreproccessor { constructor(buffer, mem_start) { this.buffer = buffer; this.mem_start = mem_start; this.program = buffer; this.program_length = buffer.length; this.address_offset = mem_start; this.pointer = -1; this.current = null; this.current_address = mem_start; this.functions = []; this.labels = []; this.sections = []; this.metadata = []; this.bintype = 0; this.finished = false; this.result = 0; this.entry_func = 0; this.entry_declared = false; this.current_section; } back() { if (this.pointer <= 0) { return this.current; } this.pointer -= 1; this.current_address = this.pointer + this.address_offset; this.current = this.program[this.pointer]; return this.current; } next() { if (this.pointer >= this.program_length) { this.finish(0); return 0x00; } this.pointer += 1; this.current_address = this.pointer + this.address_offset; this.current = this.program[this.pointer]; return this.current; } finish(exitcode) { this.finished = true; this.result = exitcode; } error(msg) { console.error(`preproccessor error: ${msg} (at ${this.pointer})`); this.finish(1); } section() { const magic_num = this.next(); if (magic_num === SECTION) { this.current_section = this.next(); } } function() { if (this.next() != 0xfa) return; const id = this.next(); let func = { address: this.current_address + 1, id: id, }; // console.log(`addr: ${this.current_address - (0xFFF + 1)}\nval: ${this.current}`); this.functions.push(func); } label() { if (this.next() != 0xfa) return; const id = this.next(); let label = { address: this.current_address + 1, id: id, }; this.labels.push(label); } binary() { if (this.bintype) return; const type = this.next(); this.bintype = type; return; } meta() { const data = this.next(); this.metadata.push(data); return; } preproccess() { this.next(); this.next(); this.next(); while (!this.finished) { if (this.current_section === 0) { switch (this.current) { case BINARY: this.binary(); break; case META: this.meta(); break; case SECTION: this.section(); break; default: this.next(); } } else if (this.current_section === 1) { switch (this.current) { case FUNC: this.function(); break; case LABEL: this.label(); break; default: this.next(); } } else { let n = this.next(); if (n === SECTION) { if (this.next() === SECTION) { this.current_section = this.next(); } } } } return this.result; } } export class KtroVirtualMachine { constructor(memory, buffer) { /* BIN TYPES 0xA1 = INLINE PROGRAM 0xA2 = INLINE ENTRY 0xF1 = PACKAGE 0xF2 = PACKAGE NO ENTRY */ /* META TYPES 0x00 = NO METADATA 0x1F = FCR (Flag Compare Results) | Instead of pushing the truthy value to the stack, | It sets the Zero Flag instead. */ this.memory = memory; this.buffer = buffer; this.MEMORY_START = 0xfff + 1; this.MEMORY_END = this.memory.length; this.HEAP_START = this.MEMORY_START + buffer.length + 0xff; this.HEAP_MAX = this.memory.length; this.STACK_BASE = 0xfff; this.MAX_ITERATIONS = buffer.length * 0xffff; this.step_debug = false; this.branch_debug = false; this.inst_debug = false; this.USE_FLAGS = false; this.ZERO_FLAG = false; this.NEG_FLAG = false; this.STDOUT = 0; this.STDIN = 1; this.registerNames = [ "rtp", "rsp", "rip", "rhp", "r10", "r9", "r8", "r7", "r6", "r5", "r4", "r3", "r2", "r1", ]; this.registers = VirtualMemoryBuffer(this.registerNames.length * 2); this.program = buffer; this.program_len = buffer.length; this.registerMap = this.registerNames.reduce((map, name, i) => { map[name] = i * 2; return map; }, {}); this.setRegister("rhp", this.HEAP_START); this.setRegister("rip", this.MEMORY_START); this.setRegister("rsp", this.STACK_BASE); this.setRegister("rtp", 0x0); this.preproccesor = new KtroPreproccessor(buffer, this.MEMORY_START); this.preprocess_result = this.preproccesor.preproccess(); if (this.preprocess_result != 0) { console.error("kvm: execution failed"); return this.preprocess_result; } this.functions = this.preproccesor.functions; this.labels = this.preproccesor.labels; this.func_entry = 0; this.bin_type = this.preproccesor.bintype; this.externs = []; this.setBinaryType(this.preproccesor.bintype); this.preproccesor.metadata.forEach((meta) => { this.addMetaData(meta); }); this.steps = 0; for (let i = 0; i < buffer.length; i++) { this.memory.setUint8(i + this.MEMORY_START, buffer[i]); //console.log(buffer[i]); } this.current_function = null; this.halt = false; this.exit_code = 0; this.current_step = 0; this.pause_execution = false; this.saved_rips = []; } reset(buffer) { this.MEMORY_START = 0xfff + 1; this.MEMORY_END = this.memory.length; this.HEAP_START = this.MEMORY_START + buffer.length + 0xff; this.HEAP_MAX = this.memory.length; this.STACK_BASE = 0xfff; this.MAX_ITERATIONS = buffer.length * 0xffff; this.step_debug = false; this.branch_debug = false; this.inst_debug = false; this.USE_FLAGS = false; this.ZERO_FLAG = false; this.NEG_FLAG = false; this.SIGNED_FLAG = false; this.STDOUT = 0; this.STDIN = 1; this.setRegister("rhp", this.HEAP_START); this.setRegister("rip", this.MEMORY_START); this.setRegister("rsp", this.STACK_BASE); this.setRegister("rtp", 0x0); this.preproccesor = new KtroPreproccessor(buffer, this.MEMORY_START); this.preprocess_result = this.preproccesor.preproccess(); if (this.preprocess_result != 0) { console.error("kvm: execution failed"); return this.preprocess_result; } this.functions = this.preproccesor.functions; this.labels = this.preproccesor.labels; this.func_entry = 0; this.bin_type = this.preproccesor.bintype; this.externs = []; this.setBinaryType(this.preproccesor.bintype); this.preproccesor.metadata.forEach((meta) => { this.addMetaData(meta); }); this.steps = 0; for (let i = 0; i < buffer.length; i++) { this.memory.setUint8(i + this.MEMORY_START, buffer[i]); //console.log(buffer[i]); } this.current_function = null; this.halt = false; this.exit_code = 0; this.current_step = 0; this.pause_execution = false; this.saved_rips = []; } addNamespace({ namespaceID, jsFunctions }) { let id = 0; jsFunctions.forEach((f) => { id += 1; this.addExtern({ namespaceID: namespaceID, methodID: id, jsFunction: f, }); }); } addExtern({ namespaceID, methodID, jsFunction }) { this.externs.push({ namespaceID: namespaceID, methodID: methodID, jsFunction: jsFunction, }); } getRegisterByNumber(r) { return this.getRegister(`r${r}`); } setBinaryType(type) { this.bin_type = type; return; } addMetaData(code) { switch (code) { case 0x00: return; case 0x1f: { this.USE_FLAGS = true; return; } } return; } storeStringToHeap(offset, str) { for (let i = 0; i < str.length; i++) { let byte = str.charCodeAt(i); this.heapStoreImm8(this.offsetAsHeapAddress(offset + i), byte); } } runExternalFunction(namespace, id) { for (let i = 0; i < this.externs.length; i++) { let ext = this.externs[i]; if (ext.namespaceID == namespace && ext.methodID == id) { let result = ext.jsFunction(this); if (result) this.push(result[1], result[0]); return 200; } } return null; } resultValue(value, type) { return [value, type]; } getArgument(type) { return this.pop(type); } useDebugger(_debugger) { switch (_debugger) { case STEP_DEBUGGER: this.step_debug = true; return; case BRANCH_DEBUGGER: this.branch_debug = true; return; case INST_DEBUGGER: this.inst_debug = true; break; } return; } debug() { if (this.branch_debug) { console.log( `step (${this.current_step + 1}) | instruction ptr: ${this.getRegister( "rip", )}`, ); return; } if (this.step_debug) { console.log(`step: ${this.current_step + 1}`); console.log(`stack ptr: ${this.getRegister("rsp")}`); console.log( `flags (${this.USE_FLAGS ? "y" : "n"}): |Z: ${this.ZERO_FLAG}|`, ); console.log("stack snapshot:"); for (let i = this.STACK_BASE; i >= 0; i--) { let value = this.memory.getUint8(i); console.log(`${i}: ${value}`); if (value == 0) break; } console.log(""); return; } } signedToUnsigned(value, bitlength) { if (value & (1 << (bitlength - 1))) { return value - (1 << bitlength); } return value; } unsignedToSigned(value, bitlength) { return value % (1 << bitlength); } lastResultTruthy() { if (this.USE_FLAGS) { return this.ZERO_FLAG; } return this.heapLoad8(this.getRegister("rsp")); } compareResult(res) { if (this.USE_FLAGS) { this.ZERO_FLAG = res ? 1 : 0; return; } this.push(8, res); } offsetAsHeapAddress(offset) { const addr = this.HEAP_START + offset; return addr >= this.HEAP_MAX ? this.HEAP_MAX : addr; } getFunction(id) { for (let i = 0; i < this.functions.length; i++) { if (this.functions[i].id === id) { return this.functions[i]; } } return null; } getLabel(id) { for (let i = 0; i < this.labels.length; i++) { if (this.labels[i].id === id) { return this.labels[i]; } } return null; } getBitSize(n) { n = n - ((n >> 1) & 0x55555555); n = (n & 0x33333333) + ((n >> 2) & 0x33333333); return (((n + (n >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; } getRegister(name) { if (!(name in this.registerMap)) { throw new Error(`getRegister: no such register '${name}'`); } return this.registers.getUint16(this.registerMap[name]); } setRegister(name, value) { if (!(name in this.registerMap)) { throw new Error(`setRegister: no such register '${name}'`); } return this.registers.setUint16(this.registerMap[name], value); } heapStoreAuto(size, offset, value) { switch (size) { case 8: return this.heapStoreImm8(offset, value); case 16: return this.heapStoreImm16(offset, value); case 32: return this.heapStoreImm32(offset, value); } console.error(`heapStoreAuto: invalid size ${size}`); return; } heapLoadAuto(size, addr) { switch (size) { case 8: return this.heapLoad8(addr); case 16: return this.heapLoad16(addr); case 32: return this.heapLoad32(addr); } console.error(`heapLoadAuto: invalid size ${size}`); return; } heapStoreImm16(addr, value) { this.setRegister("rhp", addr + 1); if (this.SIGNED_FLAG === 1) { return this.memory.setInt16(addr, value); } this.memory.setUint16(addr, value); } heapStoreImm8(addr, value) { this.setRegister("rhp", addr); if (this.SIGNED_FLAG === 1) { return this.memory.setInt8(addr, value); } this.memory.setUint8(addr, value); } heapStoreImm32(addr, value) { this.setRegister("rhp", addr + 3); if (this.SIGNED_FLAG === 1) { return this.memory.setInt32(addr, value); } this.memory.setUint32(addr, value); } heapLoad32(addr) { if (this.SIGNED_FLAG === 1) { return this.memory.getInt32(addr); } return this.memory.getUint32(addr); } heapLoad16(addr) { if (this.SIGNED_FLAG === 1) { return this.memory.getInt16(addr); } return this.memory.getUint16(addr); } heapLoad8(addr) { if (this.SIGNED_FLAG === 1) { return this.memory.getInt8(addr); } return this.memory.getUint8(addr); } fetch() { const nextInstructionAddress = this.getRegister("rip"); const instruction = this.memory.getUint8(nextInstructionAddress); this.setRegister("rip", nextInstructionAddress + 1); return instruction; } fetch16() { const nextInstructionAddress = this.getRegister("rip"); const instruction = this.memory.getUint16(nextInstructionAddress); this.setRegister("rip", nextInstructionAddress + 2); return instruction; } fetch32() { const nextInstructionAddress = this.getRegister("rip"); const instruction = this.memory.getUint32(nextInstructionAddress); this.setRegister("rip", nextInstructionAddress + 4); return instruction; } fetchBySize(size) { switch (size) { case 8: return this.fetch(); case 16: return this.fetch16(); case 32: return this.fetch32(); } console.error(`fetchBySize: invalid size ${size}`); return; } loadBufferFromHeap(offset) { const addr = this.offsetAsHeapAddress(offset); let text = ""; for (let i = 0; true; i++) { let b = this.heapLoad8(addr + i); if (b === 0) break; text += String.fromCharCode(b); } return text; } /* File mode format: 0b000 0 0 0 w|r| b|!b x|!x w = write 1 r = read 0 b = bytes 1 !b = no bytes 0 x = extend 1 !x = overwrite 0 */ getStrFileMode(mode) { // Extracting bits const m = (mode & 0b100) >> 2; // M const b = (mode & 0b010) >> 1; // B const x = mode & 0b001; // X let res = m === 1 ? "w" : "r"; res += b === 1 ? "b" : ""; res += x === 1 ? "+" : ""; return res; } interrupt() { const code = this.fetch(); switch (code) { case 0: { const stream = this.fetch(); const fp = this.loadBufferFromHeap(stream); const outputOffset = this.fetch(); const addr = this.offsetAsHeapAddress(outputOffset); const buf = fs.readFileSync(fp); let i = 0; buf.forEach((b) => { this.heapStoreAuto(8, addr + i, b); i += 1; }); return; } case 1: { const stream = this.fetch(); let fp = ""; if (stream > 0) { fp = this.loadBufferFromHeap(stream); } const offset = this.fetch(); let text = this.loadBufferFromHeap(offset); if (fp == "") process.stdout.write(text); else { fs.writeFileSync(fp, text); } return; } } } push(size, value) { let topOfStack = this.getRegister("rsp") - size / 8; switch (size) { case 8: if (this.SIGNED_FLAG === 1) this.memory.setInt8(topOfStack, value); else this.memory.setUint8(topOfStack, value); break; case 16: if (this.SIGNED_FLAG === 1) this.memory.setInt16(topOfStack, value); else this.memory.setUint16(topOfStack, value); break; case 32: if (this.SIGNED_FLAG === 1) this.memory.setInt32(topOfStack, value); else this.memory.setUint32(topOfStack, value); break; default: console.error(`push: invalid size '${size}' (value ${value})`); } if (value < 0) { if (this.USE_FLAGS) this.NEG_FLAG = true; else { topOfStack -= 1; this.memory.setUint8(topOfStack, value); } } this.setRegister("rsp", topOfStack); return; } pop(size) { let topOfStack = this.getRegister("rsp"); if (topOfStack >= this.STACK_BASE) { this.setRegister("rsp", this.STACK_BASE); topOfStack = this.getRegister("rsp"); } var value; switch (size) { case 8: { if (this.SIGNED_FLAG === 1){ value = this.memory.getInt8(topOfStack); this.memory.setInt8(topOfStack, 0); } else { value = this.memory.getUint8(topOfStack); this.memory.setUint8(topOfStack, 0); } break; } case 16: { if (this.SIGNED_FLAG === 1){ value = this.memory.getInt16(topOfStack); this.memory.setInt16(topOfStack, 0); } else { value = this.memory.getUint16(topOfStack); this.memory.setUint16(topOfStack, 0); } break; } case 32: { if (this.SIGNED_FLAG === 1) { value = this.memory.getInt32(topOfStack); this.memory.setInt32(topOfStack, 0); } else { value = this.memory.getUint32(topOfStack); this.memory.setUint32(topOfStack, 0); } break; } default: console.error(`pop (retrieve): invalid size '${size}'`); } this.setRegister("rsp", topOfStack + size / 8); if (this.SIGNED_FLAG > 0) { this.SIGNED_FLAG = 0 } return value; } error(msg) { console.error(`kvm: ${msg}`); this.exit_code = 1; this.halt = true; return; } segfault() { console.error("kvm: segmentation fault (1)"); this.exit_code = 1; this.halt = true; return; } execute(instruction) { switch (instruction) { case NOP: { return; } case INC: { const size = this.fetch(); const item = this.pop(size); this.push(size, item + 1); return; } case DEC: { const size = this.fetch(); const item = this.pop(size); this.push(size, item + 1); return; } case DUP: { const size = this.fetch(); const times = this.fetch(); const item = this.heapLoadAuto(size, this.getRegister("rsp")); for (let i = 0; i < times; i++) { this.push(size, item); } return; } case EXTERN: { const namespace = this.fetch(); const id = this.fetch(); const func = this.runExternalFunction(namespace, id); if (func == null) { this.error(`cannot find external function ${namespace}.${id}`); } return; } case CALL: { const id = this.fetch(); const func = this.getFunction(id); if (func == null) { this.error(`cannot find function ${id}`); return; } this.saved_rips.push(this.getRegister("rip")); this.setRegister("rip", func.address); return; } case RET: { this.setRegister("rip", this.saved_rips.pop()); return; } case SF: { this.SIGNED_FLAG = 1; return; } case CSF: { this.SIGNED_FLAG = 0; return; } case CZF: { this.ZERO_FLAG = 0; return; } case CAF: { this.ZERO_FLAG = 0; return; } case JMP: { const id = this.fetch(); const addr = this.getLabel(id); if (addr == null) { console.log(`error: cannot find label ${id}`); return; } this.setRegister("rip", addr.address); return; } case JZE: { const addr = this.getLabel(this.fetch()); if (this.lastResultTruthy() === 0) { this.setRegister("rip", addr.address); } return; } case CONST: { const size = this.fetch(); const value = this.fetchBySize(size); this.push(size, value); return; } case POPL: { const size = this.fetch(); this.pop(size); return; } case STO: { const size = this.fetch(); const offset = this.fetch(); const address = this.offsetAsHeapAddress(offset); const value = this.pop(size); this.heapStoreAuto(size, address, value); return; } case STD: { const size = this.fetch(); const value = this.pop(size); const offset = this.pop(size); const address = this.offsetAsHeapAddress(offset); this.heapStoreAuto(size, address, value); return; } case DB: { const size = this.fetch(); this.hoffset += size; return; } case ERASE: { const offset = this.fetch(); const size = this.fetch(); for (let i = 0; i < size; i++) { const address = this.offsetAsHeapAddress(offset + i); this.heapStoreAuto(U8, address, 0); } return; } case LDS: { const size = this.fetch(); const offset = this.fetch(); const address = this.offsetAsHeapAddress(offset); const value = this.heapLoadAuto(size, address); this.push(size, value); return; } case LDD: { const size = this.fetch(); const offset = this.pop(size); const address = this.offsetAsHeapAddress(offset); const value = this.heapLoadAuto(size, address); this.push(size, value); return; } case DEL: { const size = this.fetch(); const offset = this.fetch(); const address = this.offsetAsHeapAddress(offset); this.heapStoreAuto(size, address, 0); return; } case HLT: { const size = this.fetch(); const exit_code = this.pop(size); this.exit_code = exit_code; this.halt = true; return; } case AND: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left & right; this.push(size, res); return; } case OR: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left | right; this.push(size, res); return; } case XOR: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left ^ right; this.push(size, res); return; } case NOR: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = !(left | right); this.push(size, res); return; } case SHL: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left << right; this.push(size, res); return; } case SHR: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left >> right; this.push(size, res); return; } case ADD: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const sum = left + right; this.push(size, sum); return; } case SUB: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const sum = right - left; this.push(size, sum); return; } case MUL: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const sum = left * right; this.push(size, sum); return; } case MOD: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const sum = left & right; this.push(size, sum); return; } case CMPEQ: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left === right; this.compareResult(res); return; } case CMPNE: { const size = this.fetch(); const left = this.pop(size); const right = this.pop(size); const res = left !== right; this.compareResult(res); return; } case ASCIZ: { const size = this.fetch(); for (let i = 0; i < size; i++) { let byte = this.fetch(); this.heapStoreImm8(this.hoffset, byte); this.hoffset += 1; } return; } case LDR: { const reg = this.fetch(); const size = this.fetch(); const value = this.fetchBySize(size); this.setRegister(`r${reg}`, value); return; } case OFFSET: { const size = this.fetch(); this.hoffset += size; return; } case INTERRUPT: { this.interrupt(); return; } } } step() { if (this.pause_execution) { for (let pc = 0; true; pc++) { if (pc === 5000) console.log("program not responding."); } } this.steps += 1; const instruction = this.fetch(); if (this.inst_debug) { console.log( `[${this.steps}] (inst ${instruction .toString(16) .padStart(2, "0")}) (rip ${this.getRegister("rip")})`, ); } return this.execute(instruction); } initialize() { /* BIN TYPES 0xA1 = INLINE PROGRAM 0xA2 = INLINE ENTRY 0xF1 = PACKAGE 0xF2 = PACKAGE NO ENTRY */ this.hoffset = this.HEAP_START; switch (this.bin_type) { case 0xa1: return; case 0xa2: { const entry = this.getFunction(0); // console.log(entry); if (entry == null) { this.error("cannot find entry"); return; } this.saved_rips.push(entry.address); this.setRegister("rip", entry.address); return; } } } run() { this.initialize(); var i = 0; while (!this.halt) { this.step(); if (this.halt) break; if (i >= this.MAX_ITERATIONS) { this.segfault(); return 1; } this.debug(); // console.log(`op: ${this.getRegister("rip")-this.MEMORY_START}`); this.current_step += 1; i += 1; } if (this.step_debug) { console.log("\nHEAP SUMMARY:"); for (let i = this.HEAP_START; i <= this.getRegister("rhp") + 5; i++) { console.log(`${i}: ${this.memory.getUint8(i)}`); } console.log(""); } return this.exit_code; } } export const KtroInstance = (memory, buffer) => { return new KtroVirtualMachine(memory, buffer); };