Skip to content

Instantly share code, notes, and snippets.

@0xlkda

0xlkda/vm.js Secret

Last active June 6, 2024 08:39
Show Gist options
  • Save 0xlkda/ec4b39ad76d0f5cc6346f39e8006a710 to your computer and use it in GitHub Desktop.
Save 0xlkda/ec4b39ad76d0f5cc6346f39e8006a710 to your computer and use it in GitHub Desktop.
class CPU {
constructor(instructionSet) {
this.instructionSet = instructionSet
this.program = []
this.currentInstructionIndex = 0
this.currentInstruction = null
}
async fetch() {
const instruction = this.getNextInstruction()
this.setCurrentInstruction(instruction)
}
async execute(vm) {
const instruction = this.getCurrentInstruction()
const op = instruction.opcode
const opFunction = this.instructionSet[op]
if (!opFunction) {
console.warn('Unknown operation: ' + op)
return // Handle gracefully instead of throwing an error
}
await opFunction(instruction.args, vm)
console.log('%s %s %s', instruction.opcode, instruction.args[0], vm.memory.data[0])
}
loadProgram(program) {
this.program = program
this.currentInstructionIndex = 0
this.currentInstruction = null
}
setCurrentInstruction(instruction) {
this.currentInstruction = instruction
}
getCurrentInstruction() {
return this.currentInstruction
}
getNextInstruction() {
return this.program[this.currentInstructionIndex]
}
incrementProgramCounter() {
this.currentInstructionIndex++
}
}
class Memory {
constructor() {
this.data = []
}
setValue(value) {
this.data.push(value)
}
getValue() {
return this.data.pop()
}
setValueAt(index, value) {
this.data[index] = value
}
getValueAt(index) {
return this.data[index]
}
}
class VirtualMachine {
constructor(cpu, memory, inputFn, outputFn) {
this.cpu = cpu
this.memory = memory
this.inputFn = inputFn
this.outputFn = outputFn
this.state = {
running: false,
paused: false,
}
}
async run() {
while (this.state.running) {
if (!this.state.paused) {
await this.cpu.fetch()
await this.cpu.execute(this)
this.cpu.incrementProgramCounter()
}
await new Promise(resolve => setTimeout(resolve, 100))
}
}
start(program) {
this.state.running = true
this.state.paused = false
this.cpu.loadProgram(program)
this.run().then(() => console.log('Program halted.'))
}
stop() {
this.state.running = false
}
}
const instructionSet = {
LOAD: async (args, vm) => {
vm.memory.setValue(args[0])
},
HALT: async (args, vm) => {
vm.stop()
},
// Arithmetic operations
ADD: async (args, vm) => {
const currentValue = vm.memory.getValue()
const newValue = currentValue + args[0]
vm.memory.setValue(newValue)
},
SUBTRACT: async (args, vm) => {
const currentValue = vm.memory.getValue()
const newValue = currentValue - args[0]
vm.memory.setValue(newValue)
},
MULTIPLY: async (args, vm) => {
const currentValue = vm.memory.getValue()
const newValue = currentValue * args[0]
vm.memory.setValue(newValue)
},
DIVIDE: async (args, vm) => {
const currentValue = vm.memory.getValue()
const newValue = currentValue / args[0]
vm.memory.setValue(newValue)
},
// Conditional branching
JUMP_IF_LT_ZERO: async (args, vm) => {
let value = vm.memory.getValue()
if (value < 0) {
const targetIndex = args[0]
vm.cpu.currentInstructionIndex = targetIndex
} else vm.memory.setValue(value)
},
JUMP_IF_ZERO: async (args, vm) => {
let value = vm.memory.getValue()
if (value === 0) {
const targetIndex = args[0]
vm.cpu.currentInstructionIndex = targetIndex
} else vm.memory.setValue(value)
},
JUMP_IF_NOT_ZERO: async (args, vm) => {
let value = vm.memory.getValue()
if (value !== 0) {
const targetIndex = args[0]
vm.cpu.currentInstructionIndex = targetIndex
} else {
vm.memory.setValue(value)
}
},
// Looping
JUMP: async (args, vm) => {
const targetIndex = args[0]
vm.cpu.currentInstructionIndex = targetIndex
},
// Data manipulation
COPY: async (args, vm) => {
const sourceIndex = args[0]
const destinationIndex = args[1]
const value = vm.memory.getValueAt(sourceIndex)
vm.memory.setValueAt(destinationIndex, value)
},
MOVE: async (args, vm) => {
const sourceIndex = args[0]
const destinationIndex = args[1]
const value = vm.memory.getValueAt(sourceIndex)
vm.memory.setValueAt(sourceIndex, 0)
vm.memory.setValueAt(destinationIndex, value)
},
// Input/output
INPUT: async (args, vm) => {
const inputValue = await vm.inputFn()
vm.memory.setValue(inputValue)
},
OUTPUT: async (args, vm) => {
const outputValue = vm.memory.getValue()
await vm.outputFn(outputValue)
},
}
const program = [
{ opcode: 'LOAD', args: [5] }, // Load initial value
{ opcode: 'ADD', args: [3] }, // Add 3 to the value
{ opcode: 'JUMP_IF_ZERO', args: [5] }, // Jump to the end if value is zero
{ opcode: 'SUBTRACT', args: [1] }, // Decrement value
{ opcode: 'JUMP', args: [1] }, // Jump back to the addition
{ opcode: 'OUTPUT', args: [] }, // Print the final value
{ opcode: 'HALT', args: [] }, // HALT
]
const memory = new Memory()
const cpu = new CPU(instructionSet)
const vm = new VirtualMachine(
cpu,
memory,
async () => 42,
async (outputValue) => console.log('Output:', outputValue),
)
vm.start(program)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment