117 lines
3.1 KiB
Rust
117 lines
3.1 KiB
Rust
use crate::instruction::Opcode;
|
|
|
|
pub struct VM {
|
|
registers: [i32; 32],
|
|
pc: usize,
|
|
program: Vec<u8>,
|
|
remainder: u32,
|
|
}
|
|
|
|
impl VM {
|
|
pub fn new() -> Self {
|
|
VM {
|
|
registers: [0; 32],
|
|
pc: 0,
|
|
program: vec![],
|
|
remainder: 0,
|
|
}
|
|
}
|
|
|
|
fn decode_opcode(&mut self) -> Opcode {
|
|
let opcode = Opcode::from(self.program[self.pc]);
|
|
self.pc += 1;
|
|
opcode
|
|
}
|
|
|
|
fn next_8_bits(&mut self) -> u8 {
|
|
let result = self.program[self.pc];
|
|
self.pc += 1;
|
|
result
|
|
}
|
|
|
|
fn next_16_bits(&mut self) -> u16 {
|
|
let result = ((self.program[self.pc] as u16) << 8) | self.program[self.pc + 1] as u16;
|
|
self.pc += 2;
|
|
result
|
|
}
|
|
|
|
pub fn run(&mut self) {
|
|
let mut is_done = false;
|
|
while !is_done {
|
|
is_done = self.execute_instruction()
|
|
}
|
|
}
|
|
|
|
pub fn run_once(&mut self) {
|
|
self.execute_instruction();
|
|
}
|
|
|
|
fn execute_instruction(&mut self) -> bool {
|
|
if self.pc >= self.program.len() {
|
|
return false;
|
|
}
|
|
match self.decode_opcode() {
|
|
Opcode::LOAD => {
|
|
let reg = self.next_8_bits() as usize;
|
|
let num = self.next_16_bits() as u32;
|
|
self.registers[reg] = num as i32;
|
|
}
|
|
Opcode::HLT => {
|
|
println!("HLT encountered");
|
|
return false;
|
|
}
|
|
Opcode::IGL => {
|
|
println!("Unrecognized opcode found! Terminating!");
|
|
}
|
|
Opcode::ADD => {
|
|
let reg1 = self.registers[self.next_8_bits() as usize];
|
|
let reg2 = self.registers[self.next_8_bits() as usize];
|
|
self.registers[self.next_8_bits() as usize] = reg1 + reg2;
|
|
}
|
|
Opcode::MUL => {
|
|
let reg1 = self.registers[self.next_8_bits() as usize];
|
|
let reg2 = self.registers[self.next_8_bits() as usize];
|
|
self.registers[self.next_8_bits() as usize] = reg1 * reg2;
|
|
}
|
|
Opcode::DIV => {
|
|
let reg1 = self.registers[self.next_8_bits() as usize];
|
|
let reg2 = self.registers[self.next_8_bits() as usize];
|
|
self.registers[self.next_8_bits() as usize] = reg1 / reg2;
|
|
self.remainder = (reg1 % reg2) as u32;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod vm_tests {
|
|
use crate::vm::*;
|
|
|
|
#[test]
|
|
fn test_crate_vm() {
|
|
let test_vm = VM::new();
|
|
assert_eq!(test_vm.registers[0], 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_opcode_hlt() {
|
|
let mut test_vm = VM::new();
|
|
let test_bytes = vec![0, 0, 0, 0];
|
|
test_vm.program = test_bytes;
|
|
test_vm.run_once();
|
|
assert_eq!(test_vm.pc, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_opcode_igl() {
|
|
let mut test_vm = VM::new();
|
|
let test_bytes = vec![200, 0, 0, 0];
|
|
test_vm.program = test_bytes;
|
|
test_vm.run_once();
|
|
assert_eq!(test_vm.pc, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_load_opcode() {}
|
|
}
|