Skip to content

Instantly share code, notes, and snippets.

@matthewjberger
Created December 17, 2023 08:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewjberger/29424af9ad4393010df5fafb8f204227 to your computer and use it in GitHub Desktop.
Save matthewjberger/29424af9ad4393010df5fafb8f204227 to your computer and use it in GitHub Desktop.
Bytecode emulation pattern in rust
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