Created
December 17, 2023 08:52
-
-
Save matthewjberger/29424af9ad4393010df5fafb8f204227 to your computer and use it in GitHub Desktop.
Bytecode emulation pattern in rust
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
struct Emulator { | |
register: [u8; 2], // Two general-purpose registers | |
program_counter: usize, | |
memory: [u8; 256], // Simplified memory | |
} | |
enum Instruction { | |
Load(u8, u8), // Load a value into a register | |
Add, // Add two registers | |
Subtract, // Subtract two registers | |
JumpIfEqual(u8), // Jump to a certain memory address if registers are equal | |
Print(u8), // Print a register's value | |
Exit, // Exit program | |
} | |
impl Emulator { | |
pub fn new() -> Self { | |
Self { | |
register: [0; 2], | |
program_counter: 0, | |
memory: [0; 256], | |
} | |
} | |
pub fn run(&mut self, program: &[u8]) { | |
self.memory[..program.len()].copy_from_slice(program); | |
self.program_counter = 0; | |
loop { | |
let opcode = self.memory[self.program_counter]; | |
let instruction = match opcode { | |
0x01 => { | |
let reg = self.memory[self.program_counter + 1]; | |
let val = self.memory[self.program_counter + 2]; | |
Instruction::Load(reg, val) | |
}, | |
0x02 => Instruction::Add, | |
0x03 => Instruction::Subtract, | |
0x04 => { | |
let addr = self.memory[self.program_counter + 1]; | |
Instruction::JumpIfEqual(addr) | |
}, | |
0x05 => { | |
let reg = self.memory[self.program_counter + 1]; | |
Instruction::Print(reg) | |
}, | |
0xFF => Instruction::Exit, | |
_ => panic!("Unknown instruction"), | |
}; | |
if matches!(instruction, Instruction::Exit) { | |
break; | |
} | |
self.execute(instruction); | |
self.program_counter += match opcode { | |
0x01 => 3, // Load instruction has 3 bytes | |
0x04 | 0x05 => 2, // Jump and Print have 2 bytes | |
_ => 1, // Other instructions have 1 byte | |
}; | |
} | |
} | |
fn execute(&mut self, instruction: Instruction) { | |
match instruction { | |
Instruction::Load(reg, val) => self.register[reg as usize] = val, | |
Instruction::Add => self.register[0] = self.register[0].wrapping_add(self.register[1]), | |
Instruction::Subtract => self.register[0] = self.register[0].wrapping_sub(self.register[1]), | |
Instruction::JumpIfEqual(addr) => { | |
if self.register[0] == self.register[1] { | |
self.program_counter = addr as usize; | |
} | |
} | |
Instruction::Print(reg) => println!("Register[{}] = {}", reg, self.register[reg as usize]), | |
Instruction::Exit => (), | |
} | |
} | |
} | |
fn main() { | |
// Testing the emulator | |
let mut emulator = Emulator::new(); | |
// A program to demonstrate various operations | |
let program = [ | |
0x01, 0x00, 0x05, // Load 5 into register 0 | |
0x01, 0x01, 0x03, // Load 3 into register 1 | |
0x02, // Add register 0 and 1 | |
0x05, 0x00, // Print register 0 | |
0x03, // Subtract register 1 from register 0 | |
0x05, 0x00, // Print register 0 | |
0x04, 0x10, // Jump to address 0x10 if register 0 equals register 1 | |
0xFF, // Exit | |
// Address 0x10 | |
0x05, 0x01, // Print register 1 (unreachable unless registers are equal) | |
0xFF, // Exit | |
]; | |
emulator.run(&program); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment