-
-
Save 0xlkda/ec4b39ad76d0f5cc6346f39e8006a710 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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